├── .gitignore ├── LICENSE ├── README.md ├── ST7735.py ├── bme680.py ├── bmx280.py ├── fonts ├── glcdfont.py ├── tt14.py ├── tt24.py └── tt32.py ├── hcsr04.py ├── ili934xnew.py ├── imu.py ├── mpu9250.py ├── pmsa003.py ├── sdcard.py ├── ssd1306.py ├── ssd1306_i2c.py ├── stepper.py ├── sysfont.py ├── ublox_gps.py ├── vector3d.py └── vl53l0x.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # compiled files 107 | *.mpy 108 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uPySensors 2 | MicroPython sensor/actuator libraries 3 | 4 | 5 | # Added Drivers 6 | 7 | | | | | 8 | |:--|:--|:--| 9 | |`hcsr04.py`|Ultrasonic Sensor - HC-SR04 | [about](https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf) - [buy](https://www.banggood.com/de/Wholesale-Ultrasonic-Module-HC-SR04-Distance-Measuring-Ranging-Transducer-Sensor-p-40313.html?p=QW0903761303201409LG) - [blog article](https://lemariva.com/blog/2018/06/tutorial-getting-started-with-micropython-sensors)| 10 | |`imu.py & vector3d.py`| MPU6055 - Triple Axis Accelerometer and Gyro |[about](https://www.invensense.com/products/motion-tracking/6-axis/mpu-6500/) - [buy](https://www.banggood.com/de/5pcs-GY-6500-MPU6500-6DOF-6-Axis-Attitude-Acceleration-Gyroscope-Sensor-Module-SPI-Interface-p-1291399.html?p=QW0903761303201409LG) - [blog article](https://lemariva.com/blog/2018/06/micropython-camera-stabilisation-application)|| 11 | |`imu.py, vector3d.py & mpu9250.py`| MPU9250/5 - 9-axis Motion Processing Unit |[about](https://www.invensense.com/products/motion-tracking/9-axis/mpu-9250/) - [buy](https://www.banggood.com/de/GY-91-MPU9250-BMP280-10DOF-Acceleration-Gyroscope-Compass-Nine-Shaft-Sensor-Module-p-1129541.html?p=QW0903761303201409LG) - [blog article](https://lemariva.com/blog/2018/06/micropython-camera-stabilisation-application)| 12 | |`ublox_gps.py` | NEO-M8N/6M - GPS Modules |[about](https://www.u-blox.com/de/product/neo-m8-series) - [buy](https://rover.ebay.com/rover/1/707-53477-19255-0/1?toolid=20001&campid=5338002758&customid=link&mpre=http%3A%2F%2Fwww.ebay.de%2Fitm%2F272470133068%3F_trksid%3Dp2060353.m2749.l2649%26ssPageName%3DSTRK%253AMEBIDX%253AIT) - [blog article](https://lemariva.com/blog/2017/04/wipy-2-0-weather-report-neo-m8n)| 13 | |`vl53l0x.py` | Vl53l0X - laser-ranging module |[about](http://www.st.com/en/imaging-and-photonics-solutions/vl53l0x.html) - [buy](https://www.banggood.com/de/GY-530-VL53L0X-Laser-Ranging-Sensor-Module-IIC-Communication-Ranging-Module-p-1201341.html?p=QW0903761303201409LG) - [blog article](https://lemariva.com/blog/2018/12/happy-new-year-2019)| 14 | |`ssd1306_i2c.py` `ssd1306.py` | SSD1306 - display drivers | [blog article](https://lemariva.com/blog/2018/10/micropython-esp32-sending-data-using-lora) | 15 | |`ST7735.py` `sysfont.py` | ST7735 - display drivers for ESP32/ESP8266 | [blog article](https://lemariva.com/blog/2019/01/micropython-programming-an-esp-using-jupyter-notebook) | 16 | |`ili934xnew.py` `fonts/*` | ILI934 - display drivers for ESP32/M5Stack | [blog article](https://lemariva.com/blog/2020/02/m5stack-micropython-and-bluetooth-ble) | 17 | |`bmx280.py` | BME280 / BMP280 - temperature, pressure and humidity (BME) sensor| [about](https://www.bosch-sensortec.com/products/environmental-sensors/humidity-sensors-bme280/) - [blog article soon] | 18 | |`bmx680.py` | BME680 - gas, temperature, pressure and humidity sensor| [about](https://www.bosch-sensortec.com/products/environmental-sensors/gas-sensors-bme680/) - [blog article](https://lemariva.com/blog/default/default/micropython-google-cloud-platform-getting-data-m5stack-atom-sensing-air-quality) | 19 | |`pmsa003.py` | PMSA003(a) particle/dust sensor| [about](https://lemariva.com/storage/app/media/uploaded-files/PMSA003.pdf) - [blog article](https://lemariva.com/blog/2020/04/micropython-google-cloud-platform-getting-data-m5stack-atom-sensing-air-quality) | 20 | |`stepper.py` | 6-wire steppers using LN298| [blog article soon] | 21 | # Licenses 22 | * check files 23 | -------------------------------------------------------------------------------- /bme680.py: -------------------------------------------------------------------------------- 1 | ''' 2 | ''' 3 | # Power Modes 4 | from machine import I2C, Pin 5 | from micropython import const 6 | from ustruct import unpack as unp 7 | import utime 8 | 9 | epoch_offset = 946684800 10 | 11 | NORMAL = const(0) 12 | 13 | # BME680 Temperature Registers 14 | BME680_REG_DIG_T1 = const(0xE9) 15 | BME680_REG_DIG_T2 = const(0x8A) 16 | BME680_REG_DIG_T3 = const(0x8C) 17 | # BME680 Pressure Registers 18 | BME680_REG_DIG_P1 = const(0x8E) 19 | BME680_REG_DIG_P2 = const(0x90) 20 | BME680_REG_DIG_P3 = const(0x92) 21 | BME680_REG_DIG_P4 = const(0x94) 22 | BME680_REG_DIG_P5 = const(0x96) 23 | BME680_REG_DIG_P6 = const(0x99) 24 | BME680_REG_DIG_P7 = const(0x98) 25 | BME680_REG_DIG_P8 = const(0x9C) 26 | BME680_REG_DIG_P9 = const(0x9E) 27 | BME680_REG_DIG_P10 = const(0xA0) 28 | # BME680 Humidity Registers 29 | BME680_REG_DIG_H1_LSB = const(0xE1) 30 | BME680_REG_DIG_H1_MSB = const(0xE3) 31 | BME680_REG_DIG_H2_LSB = const(0xE2) 32 | BME680_REG_DIG_H2_MSB = const(0xE1) 33 | BME680_REG_DIG_H3 = const(0xE4) 34 | BME680_REG_DIG_H4 = const(0xE5) 35 | BME680_REG_DIG_H5 = const(0xE6) 36 | BME680_REG_DIG_H6 = const(0xE7) 37 | BME680_REG_DIG_H7 = const(0xE8) 38 | # BME680 Gas Sensor 39 | BME680_REG_DIG_G1 = const(0xED) 40 | BME680_REG_DIG_G2 = const(0xE7) 41 | BME680_REG_DIG_G3 = const(0xEE) 42 | 43 | BME680_REG_ID = const(0xD0) 44 | BME680_NEW_DATA_MSK = const(0x80) 45 | BME680_REG_RESET = const(0xE0) 46 | BME680_RES_HEAT_0 = const(0x5A) 47 | BME680_GAS_WAIT_0 = const(0x64) 48 | BME680_HEAT_STAB_MSK = const(0x10) 49 | 50 | BME680_GAS_INDEX_MSK = const(0x0F) 51 | BME680_GAS_RANGE_MSK = const(0x0F) 52 | BME680_GASM_VALID_MSK = const(0x20) 53 | 54 | BME680_REG_CTRL_GAS = const(0x71) 55 | BME680_REG_CTRL_HUM = const(0x72) 56 | BME680_REG_STATUS = const(0xF3) 57 | BME680_REG_CTRL_MEAS = const(0x74) 58 | BME680_REG_CONFIG = const(0x75) # IIR filter config 59 | 60 | BME680_REG_MEAS_STATUS = const(0x1D) 61 | BME680_REG_PDATA = const(0x1F) 62 | BME680_REG_TDATA = const(0x22) 63 | BME680_REG_HDATA = const(0x25) 64 | BME680_MAX_OVERFLOW_VAL =const(0x40000000) 65 | 66 | BMP680_I2C_ADDR = const(0x77) 67 | 68 | BME680_SAMPLERATES = (0, 1, 2, 4, 8, 16) 69 | BME680_FILTERSIZES = (0, 1, 3, 7, 15, 31, 63, 127) 70 | BME680_RUNGAS = const(0x10) 71 | 72 | _LOOKUP_TABLE_1 = ( 73 | 2147483647.0, 74 | 2147483647.0, 75 | 2147483647.0, 76 | 2147483647.0, 77 | 2147483647.0, 78 | 2126008810.0, 79 | 2147483647.0, 80 | 2130303777.0, 81 | 2147483647.0, 82 | 2147483647.0, 83 | 2143188679.0, 84 | 2136746228.0, 85 | 2147483647.0, 86 | 2126008810.0, 87 | 2147483647.0, 88 | 2147483647.0, 89 | ) 90 | 91 | _LOOKUP_TABLE_2 = ( 92 | 4096000000.0, 93 | 2048000000.0, 94 | 1024000000.0, 95 | 512000000.0, 96 | 255744255.0, 97 | 127110228.0, 98 | 64000000.0, 99 | 32258064.0, 100 | 16016016.0, 101 | 8000000.0, 102 | 4000000.0, 103 | 2000000.0, 104 | 1000000.0, 105 | 500000.0, 106 | 250000.0, 107 | 125000.0, 108 | ) 109 | 110 | class MPUException(OSError): 111 | ''' 112 | Exception for MPU devices 113 | ''' 114 | pass 115 | 116 | class GasSettings: 117 | def __init__(self): 118 | # Variable to store nb conversion 119 | self.nb_conv = None 120 | # Variable to store heater control 121 | self.heatr_ctrl = None 122 | # Run gas enable value 123 | self.run_gas = None 124 | # Pointer to store heater temperature 125 | self.heatr_temp = None 126 | # Pointer to store duration profile 127 | self.heatr_dur = None 128 | 129 | class TPHSettings: 130 | def __init__(self): 131 | # Humidity oversampling 132 | self.os_hum = None 133 | # Temperature oversampling 134 | self.os_temp = None 135 | # Pressure oversampling 136 | self.os_pres = None 137 | # Filter coefficient 138 | self.filter = None 139 | 140 | class BME680(): 141 | _I2Cerror = "I2C failure when communicating with the BMP/E" 142 | # BME680 = 0x61 143 | _chip_id = 0x61 144 | 145 | _i2c_addr = BMP680_I2C_ADDR 146 | 147 | def __init__(self, i2c, pins): 148 | 149 | self._pins = pins 150 | 151 | self._buf1 = bytearray(1) 152 | self._buf2 = bytearray(2) 153 | 154 | scl_pin = Pin(self._pins["scl"], Pin.OUT) 155 | sda_pin = Pin(self._pins["sda"], Pin.IN) 156 | 157 | self._i2c = I2C(i2c, scl=scl_pin, sda=sda_pin) 158 | 159 | self.chip_id 160 | 161 | self._load_calibration() 162 | 163 | self.power_on() 164 | 165 | # Sensor settings 166 | self.tph_settings = TPHSettings() 167 | # Gas Sensor settings 168 | self.gas_settings = GasSettings() 169 | 170 | # Default oversampling and filter register values. 171 | self.tph_settings.os_pres = 0b011 172 | self.tph_settings.os_temp = 0b100 173 | self.tph_settings.os_hum = 0b010 174 | self.tph_settings.filter = 0b010 175 | 176 | # gas measurements enabled 177 | self._write(BME680_REG_CTRL_GAS, BME680_RUNGAS) 178 | 179 | # RAW measurements 180 | self._p_raw = 0 181 | self._t_raw = 0 182 | self._h_raw = 0 183 | self._g_raw = 0 184 | 185 | # Calibrated measurements 186 | self._t_fine = 0 187 | self._t = 0 188 | self._h = 0 189 | self._p = 0 190 | self._g = 0 191 | 192 | self._g_range = 0 193 | self._g_stable = 0 194 | 195 | self._read_wait_ms = 100 196 | self._new_read_ms = 200 197 | self._last_read_ts = 0 198 | 199 | def _read(self, memaddr, size=1): 200 | data = self._i2c.readfrom_mem(self._i2c_addr, memaddr, size) 201 | return data 202 | 203 | def _write(self, addr, b_arr): 204 | if not type(b_arr) is bytearray: 205 | b_arr = bytearray([b_arr]) 206 | return self._i2c.writeto_mem(self._i2c_addr, addr, b_arr) 207 | 208 | def _load_calibration(self): 209 | # read calibration data 210 | # < little-endian 211 | # H unsigned short 212 | # h signed short 213 | self._T1 = unp(' self._new_read_ms: 285 | # set filter 286 | self._write(BME680_REG_CONFIG, self.tph_settings.filter << 2) 287 | # turn on temp oversample & pressure oversample 288 | self._write( 289 | BME680_REG_CTRL_MEAS, 290 | (self.tph_settings.os_temp << 5) | (self.tph_settings.os_pres << 2), 291 | ) 292 | utime.sleep_ms(100) 293 | 294 | # turn on humidity oversample 295 | self._write(BME680_REG_CTRL_HUM, self.tph_settings.os_hum) 296 | 297 | ctrl = unp('> 4) 310 | self._t_raw = (regs[5] << 12) | (regs[6] << 4) | (regs[7] >> 4) 311 | self._h_raw = (regs[8] << 8) | regs[9] 312 | 313 | self._g_raw = (regs[13] << 2) | (regs[14] >> 6) 314 | self._g_range = regs[14] & BME680_GAS_RANGE_MSK 315 | self._g_stable = (data_status & BME680_HEAT_STAB_MSK) > 0 316 | 317 | self._t_fine = 0 318 | self._t = 0 319 | self._g = 0 320 | self._h = 0 321 | self._p = 0 322 | 323 | def _calc_t_fine(self): 324 | # From datasheet page 22 325 | self._gauge() 326 | if self._t_fine == 0: 327 | var1 = (((self._t_raw / 16384.0) - (self._T1 / 1024.0)) * self._T2) 328 | var2 = ((((self._t_raw / 131072.0) - (self._T1 / 8192.0)) * 329 | ((self._t_raw / 131072.0) - (self._T1 / 8192.0))) * 330 | (self._T3 * 16.0)) 331 | self._t_fine = var1 + var2 332 | 333 | self._t = ((self._t_fine * 5) + 128) / 256 / 100 334 | 335 | 336 | def set_gas_heater_profile(self, temperature, duration, nb_profile=0): 337 | 338 | if nb_profile > 9 or nb_profile < 0: 339 | raise ValueError("Profile '{}' should be between {} and {}".format(nb_profile, 0, 9)) 340 | 341 | # set temperature 342 | self.gas_settings.heatr_temp = temperature 343 | temp = int(self._calc_heater_resistance(temperature)) 344 | self._write(BME680_RES_HEAT_0+ nb_profile, temp) 345 | 346 | # set duration 347 | self.gas_settings.heatr_dur = duration 348 | temp = self._calc_heater_duration(duration) 349 | self._write(BME680_GAS_WAIT_0 + nb_profile, temp) 350 | 351 | 352 | def _calc_heater_resistance(self, temperature): 353 | temperature = min(max(temperature,200),400) 354 | 355 | var1 = ((self._t * self._G3) / 1000) * 256 356 | var2 = (self._G1 + 784) * (((((self._G2 + 154009) * temperature * 5) / 100) + 3276800) / 10) 357 | var3 = var1 + (var2 / 2) 358 | var4 = (var3 / (self._g_range + 4)) 359 | var5 = (131 * self._heat_val) + 65536 360 | heatr_res_x100 = (((var4 / var5) - 250) * 34) 361 | heatr_res = ((heatr_res_x100 + 50) / 100) 362 | 363 | return heatr_res 364 | 365 | def _calc_heater_duration(self, duration): 366 | if duration < 0xfc0: 367 | factor = 0 368 | 369 | while duration > 0x3f: 370 | duration /= 4 371 | factor += 1 372 | 373 | return int(duration + (factor * 64)) 374 | 375 | return 0xff 376 | 377 | @property 378 | def gas(self): 379 | """The gas resistance in ohms""" 380 | self._calc_t_fine() 381 | if self._g == 0: 382 | var1 = int( 383 | (1340 + (5 * self._sw_err)) * (_LOOKUP_TABLE_1[self._g_range]) 384 | ) >> 16 385 | var2 = ((self._g_raw << 15) - 16777216) + var1 386 | var3 = int(_LOOKUP_TABLE_2[self._g_range] * var1) >> 9 387 | self._g = (var3 + (var2 / 2)) / var2 388 | 389 | return self._g 390 | 391 | @property 392 | def filter_size(self): 393 | """The filter size for the built in IIR filter""" 394 | return BME680_FILTERSIZES[self._filter] 395 | 396 | @property 397 | def humidity(self): 398 | """The relative humidity in RH %""" 399 | self._calc_t_fine() 400 | if self._h == 0: 401 | temp_scaled = ((self._t_fine * 5) + 128) / 256 402 | var1 = (self._h_raw - (self._H1 * 16)) - ( 403 | (temp_scaled * self._H3) / 200 404 | ) 405 | var2 = ( 406 | self._H2 407 | * ( 408 | ((temp_scaled * self._H4) / 100) 409 | + ( 410 | ( 411 | ( 412 | temp_scaled 413 | * ((temp_scaled * self._H5) / 100) 414 | ) 415 | / 64 416 | ) 417 | / 100 418 | ) 419 | + 16384 420 | ) 421 | ) / 1024 422 | var3 = var1 * var2 423 | var4 = self._H6 * 128 424 | var4 = (var4 + ((temp_scaled * self._H7) / 100)) / 16 425 | var5 = ((var3 / 16384) * (var3 / 16384)) / 1024 426 | var6 = (var4 * var5) / 2 427 | calc_hum = (((var3 + var6) / 1024) * 1000) / 4096 428 | self._h = calc_hum / 1000 # get back to RH 429 | return self._h 430 | 431 | @property 432 | def temperature(self): 433 | self._calc_t_fine() 434 | self._t = ((self._t_fine * 5) + 128) / 256 / 100 435 | return self._t 436 | 437 | @property 438 | def pressure(self): 439 | self._calc_t_fine() 440 | if self._p == 0: 441 | var1 = (int(self._t_fine) >> 1) - 64000 442 | var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * self._P6) >> 2 443 | var2 = var2 + ((var1 * self._P5) << 1) 444 | var2 = (var2 >> 2) + (self._P4 << 16) 445 | var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) * \ 446 | (self._P3 << 5)) >> 3) + ((self._P2 * var1) >> 1) 447 | var1 = var1 >> 18 448 | var1 = ((32768 + var1) * self._P1) >> 15 449 | pressure_comp = 1048576 - self._p_raw 450 | pressure_comp = int((pressure_comp - (var2 >> 12)) * 3125) 451 | 452 | if pressure_comp >= BME680_MAX_OVERFLOW_VAL: 453 | pressure_comp = (int(pressure_comp / var1) << 1) 454 | else: 455 | pressure_comp = (pressure_comp << 1) / var1 456 | 457 | 458 | var1 = (self._P9 * ((pressure_comp >> 3) * \ 459 | (pressure_comp >> 3)) >> 13) >> 12 460 | var2 = ((pressure_comp >> 2) * self._P8) >> 13 461 | var3 = ((pressure_comp >> 8) * (pressure_comp >> 8) * \ 462 | (pressure_comp >> 8) * self._P10) >> 17 463 | 464 | self._p = (pressure_comp + ((var1 + var2 + var3 + (self._P7 << 7)) >> 4)) / 100 465 | 466 | return self._p 467 | 468 | @property 469 | def measurements(self): 470 | d = {} 471 | d['time'] = utime.time() + epoch_offset 472 | d['temp'] = self.temperature 473 | d['hum'] = self.humidity 474 | d['press'] = self.pressure 475 | d['gas'] = self.gas 476 | return d 477 | 478 | @property 479 | def chip_id(self): 480 | ''' 481 | Returns Chip ID 482 | ''' 483 | try: 484 | chip_id = unp('> 20 137 | self._H4 = h4 | ( 138 | unp('> 20 142 | self._H5 = h5 | ( 143 | unp('> 4 & 0x0F) 144 | 145 | 146 | def print_calibration(self): 147 | print("T1: {} {}".format(self._T1, type(self._T1))) 148 | print("T2: {} {}".format(self._T2, type(self._T2))) 149 | print("T3: {} {}".format(self._T3, type(self._T3))) 150 | print("P1: {} {}".format(self._P1, type(self._P1))) 151 | print("P2: {} {}".format(self._P2, type(self._P2))) 152 | print("P3: {} {}".format(self._P3, type(self._P3))) 153 | print("P4: {} {}".format(self._P4, type(self._P4))) 154 | print("P5: {} {}".format(self._P5, type(self._P5))) 155 | print("P6: {} {}".format(self._P6, type(self._P6))) 156 | print("P7: {} {}".format(self._P7, type(self._P7))) 157 | print("P8: {} {}".format(self._P8, type(self._P8))) 158 | print("P9: {} {}".format(self._P9, type(self._P9))) 159 | if self._chip_id != 0x58: 160 | print("H1: {} {}".format(self._H1, type(self._H1))) 161 | print("H2: {} {}".format(self._H2, type(self._H2))) 162 | print("H3: {} {}".format(self._H3, type(self._H3))) 163 | print("H4: {} {}".format(self._H4, type(self._H4))) 164 | print("H5: {} {}".format(self._H5, type(self._H5))) 165 | print("H6: {} {}".format(self._H6, type(self._H6))) 166 | 167 | def power_off(self): 168 | self._write(0xF4, 0) 169 | 170 | # normal mode 171 | def power_on(self): 172 | self._write(0xF4, 0x2F) 173 | 174 | def _gauge(self): 175 | now = utime.ticks_ms() 176 | if utime.ticks_diff(now, self._last_read_ts) > self._new_read_ms: 177 | self._last_read_ts = now 178 | r = self._t_os + (self._p_os << 3) + (1 << 6) 179 | self._write(BMX280_REGISTER_CONTROL, r) 180 | utime.sleep_ms(100) # TODO calc sleep 181 | if self._chip_id == 0x58: 182 | d = self._read(BMX280_REGISTER_DATA, 6) # read all data at once (as by spec) 183 | self._p_raw = (d[0] << 12) + (d[1] << 4) + (d[2] >> 4) 184 | self._t_raw = (d[3] << 12) + (d[4] << 4) + (d[5] >> 4) 185 | else: 186 | d = self._read(BMX280_REGISTER_DATA, 8) # read all data at once (as by spec) 187 | self._p_raw = (d[0] << 12) + (d[1] << 4) + (d[2] >> 4) 188 | self._t_raw = (d[3] << 12) + (d[4] << 4) + (d[5] >> 4) 189 | self._h_raw = (d[6] << 8) + d[7] 190 | 191 | self._t_fine = 0 192 | self._t = 0 193 | self._h = 0 194 | self._p = 0 195 | 196 | def _calc_t_fine(self): 197 | # From datasheet page 22 198 | self._gauge() 199 | if self._t_fine == 0: 200 | var1 = (((self._t_raw >> 3) - (self._T1 << 1)) * self._T2) >> 11 201 | var2 = (((((self._t_raw >> 4) - self._T1) * ((self._t_raw >> 4) - self._T1)) >> 12) * self._T3) >> 14 202 | self._t_fine = var1 + var2 203 | 204 | @property 205 | def humidity(self): 206 | if self._chip_id != 0x58: 207 | var1 = self._calc_t_fine - 76800 208 | var1 = (((((self._h_raw << 14) - (self._H5 << 20) - (self._H5 * var1)) + 209 | 16384) >> 15) * (((((((var1 * self._H6) >> 10) * (((var1 * 210 | self._H3) >> 11) + 32768)) >> 10) + 2097152) * 211 | self._H2 + 8192) >> 14)) 212 | var1 = var1 - (((((var1 >> 15) * (var1 >> 15)) >> 7) * self._H1) >> 4) 213 | var1 = 0 if var1 < 0 else var1 214 | var1 = 419430400 if var1 > 419430400 else var1 215 | return var1 >> 12 216 | else: 217 | print("This is a BMP not a BME, therefore it cannot measure humidity! :(") 218 | return 0 219 | 220 | @property 221 | def temperature(self): 222 | self._calc_t_fine() 223 | if self._t == 0: 224 | self._t = ((self._t_fine * 5 + 128) >> 8) / 100. 225 | return self._t 226 | 227 | @property 228 | def pressure(self): 229 | # From datasheet page 22 (BMP) /25 (BME) 230 | self._calc_t_fine() 231 | if self._p == 0: 232 | var1 = self._t_fine - 128000 233 | var2 = var1 * var1 * self._P6 234 | var2 = var2 + ((var1 * self._P5) << 17) 235 | var2 = var2 + (self._P4 << 35) 236 | var1 = ((var1 * var1 * self._P3) >> 8) + ((var1 * self._P2) << 12) 237 | var1 = (((1 << 47) + var1) * self._P1) >> 33 238 | 239 | if var1 == 0: 240 | return 0 241 | 242 | p = 1048576 - self._p_raw 243 | p = int((((p << 31) - var2) * 3125) / var1) 244 | var1 = (self._P9 * (p >> 13) * (p >> 13)) >> 25 245 | var2 = (self._P8 * p) >> 19 246 | 247 | p = ((p + var1 + var2) >> 8) + (self._P7 << 4) 248 | self._p = p / 256.0 249 | return self._p 250 | 251 | 252 | @property 253 | def chip_id(self): 254 | ''' 255 | Returns Chip ID 256 | ''' 257 | try: 258 | chip_id = unp('= 32 and ordch <= 126 else 32 148 | offset = _chr_addr(ordch) 149 | width += int.from_bytes(_font[offset:offset + 2], 'little') 150 | return width 151 | 152 | def get_ch(ch): 153 | ordch = ord(ch) 154 | ordch = ordch + 1 if ordch >= 32 and ordch <= 126 else 32 155 | offset = _chr_addr(ordch) 156 | width = int.from_bytes(_font[offset:offset + 2], 'little') 157 | next_offs = _chr_addr(ordch +1) 158 | return _mvfont[offset + 2:next_offs], width 159 | 160 | -------------------------------------------------------------------------------- /fonts/tt24.py: -------------------------------------------------------------------------------- 1 | # Code generated by font-to-py.py. 2 | # Font: CM Sans Serif 2012.ttf 3 | version = '0.2' 4 | 5 | def height(): 6 | return 24 7 | 8 | def max_width(): 9 | return 20 10 | 11 | def hmap(): 12 | return False 13 | 14 | def reverse(): 15 | return False 16 | 17 | def monospaced(): 18 | return False 19 | 20 | def min_ch(): 21 | return 32 22 | 23 | def max_ch(): 24 | return 126 25 | 26 | _font =\ 27 | b'\x0c\x00\xc0\x00\x00\xf0\x00\x00\xf0\x00\x00\x78\x70\x07\x38\x78'\ 28 | b'\x07\x38\x7c\x07\x78\x1e\x00\xf0\x0f\x00\xf0\x07\x00\xc0\x03\x00'\ 29 | b'\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 30 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00'\ 31 | b'\x00\x02\xf8\x3f\x07\xf8\x3f\x07\xf8\x3f\x02\x00\x00\x00\x00\x00'\ 32 | b'\x00\x08\x00\xf8\x03\x00\xf8\x03\x00\x08\x00\x00\x00\x00\x00\xf8'\ 33 | b'\x03\x00\xf8\x03\x00\x08\x00\x00\x00\x00\x00\x0c\x00\x00\xe0\x00'\ 34 | b'\x80\xe3\x00\x80\xf3\x03\xe0\xff\x03\xe0\xff\x00\xe0\xe3\x00\x80'\ 35 | b'\xe3\x03\x80\xff\x03\xe0\xff\x01\xe0\xe7\x00\x80\xe3\x00\x80\x03'\ 36 | b'\x00\x0b\x00\x00\xe0\x00\xc0\xe3\x01\xe0\xc7\x01\x70\x8e\x03\xf8'\ 37 | b'\xef\x07\xf8\xff\x07\x70\x8c\x03\xe0\x9d\x01\xc0\xf9\x01\x00\xf0'\ 38 | b'\x00\x00\x00\x00\x12\x00\xe0\x01\x00\xf0\x03\x00\x38\x07\x00\x38'\ 39 | b'\x07\x04\x38\x07\x07\x38\xc7\x07\xf0\xf3\x01\xe0\x79\x00\x00\x1e'\ 40 | b'\x00\x80\x07\x00\xe0\xe3\x01\xf8\xf0\x03\x38\x38\x07\x08\x38\x07'\ 41 | b'\x00\x38\x07\x00\xf0\x03\x00\xe0\x01\x00\x00\x00\x0f\x00\x00\xf0'\ 42 | b'\x00\x00\xf8\x01\xe0\xfd\x03\xf0\x8f\x07\xf8\x07\x07\x38\x0e\x07'\ 43 | b'\x38\x1f\x07\xf8\x3f\x07\xf0\xfb\x07\xe0\xf0\x07\x00\xe0\x03\x00'\ 44 | b'\xf8\x07\x00\xf8\x07\x00\x78\x06\x00\x00\x04\x04\x00\xf8\x03\x00'\ 45 | b'\xf8\x03\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x7f\x00\xe0\xff'\ 46 | b'\x03\xf8\xff\x0f\x7c\x00\x1f\x0e\x00\x38\x02\x00\x20\x02\x00\x20'\ 47 | b'\x07\x00\x02\x00\x20\x0e\x00\x38\x7c\x00\x1f\xf8\xff\x0f\xe0\xff'\ 48 | b'\x03\x00\x7f\x00\x00\x00\x00\x09\x00\x60\x00\x00\x60\x00\x00\x78'\ 49 | b'\x03\x00\xf0\x01\x00\xe0\x00\x00\xf8\x03\x00\x68\x01\x00\x60\x00'\ 50 | b'\x00\x00\x00\x00\x0c\x00\x00\x38\x00\x00\x38\x00\x00\x38\x00\x00'\ 51 | b'\x38\x00\x80\xff\x03\x80\xff\x03\x80\xff\x03\x00\x38\x00\x00\x38'\ 52 | b'\x00\x00\x38\x00\x00\x38\x00\x00\x00\x00\x05\x00\x00\x00\x37\x00'\ 53 | b'\x00\x3f\x00\x00\x1f\x00\x00\x00\x00\x00\x00\x07\x00\x00\x38\x00'\ 54 | b'\x00\x38\x00\x00\x38\x00\x00\x38\x00\x00\x38\x00\x00\x38\x00\x00'\ 55 | b'\x38\x00\x05\x00\x00\x00\x07\x00\x00\x07\x00\x00\x07\x00\x00\x00'\ 56 | b'\x00\x00\x00\x08\x00\x00\x00\x06\x00\xe0\x07\x00\xfc\x07\xc0\xff'\ 57 | b'\x01\xf8\x3f\x00\xfc\x03\x00\x7c\x00\x00\x04\x00\x00\x0d\x00\x80'\ 58 | b'\x7f\x00\xe0\xff\x01\xf0\xff\x03\xf8\xc0\x07\x38\x00\x07\x38\x00'\ 59 | b'\x07\x38\x00\x07\xf8\xc0\x07\xf0\xff\x03\xe0\xff\x01\x80\x7f\x00'\ 60 | b'\x00\x00\x00\x00\x00\x00\x08\x00\xc0\x01\x00\xc0\x01\x00\xe0\x01'\ 61 | b'\x00\xf8\xff\x07\xf8\xff\x07\xf8\xff\x07\x00\x00\x00\x00\x00\x00'\ 62 | b'\x0d\x00\xc0\x81\x07\xe0\xe1\x07\xf0\xe1\x07\x78\x70\x07\x38\x38'\ 63 | b'\x07\x38\x38\x07\x38\x1c\x07\x78\x1e\x07\xf0\x0f\x07\xf0\x07\x07'\ 64 | b'\xc0\x03\x07\x00\x00\x00\x00\x00\x00\x0c\x00\xc0\xc0\x00\xf0\xc0'\ 65 | b'\x03\xf0\xc0\x03\x78\x80\x07\x38\x00\x07\x38\x0e\x07\x38\x0e\x07'\ 66 | b'\x78\x9e\x07\xf0\xff\x03\xf0\xff\x03\xe0\xf1\x00\x00\x00\x00\x0c'\ 67 | b'\x00\x00\xf0\x00\x00\xf8\x00\x00\xfe\x00\x00\xef\x00\xc0\xe7\x00'\ 68 | b'\xf0\xe1\x00\xf8\xe0\x00\xf8\xff\x07\xf8\xff\x07\xf8\xff\x07\x00'\ 69 | b'\xe0\x00\x00\xe0\x00\x0c\x00\x00\xce\x00\xf8\xcf\x03\xf8\xcf\x03'\ 70 | b'\x38\x8e\x07\x38\x07\x07\x38\x07\x07\x38\x07\x07\x38\x8f\x07\x38'\ 71 | b'\xfe\x03\x38\xfc\x01\x00\xf8\x00\x00\x00\x00\x0d\x00\x00\x7f\x00'\ 72 | b'\xe0\xff\x01\xf0\xff\x03\x78\x8e\x07\x38\x07\x07\x38\x07\x07\x38'\ 73 | b'\x07\x07\x78\x8f\x07\xf0\xfe\x03\xe0\xfc\x01\xc0\xf8\x00\x00\x00'\ 74 | b'\x00\x00\x00\x00\x0b\x00\x38\x00\x00\x38\x00\x00\x38\xc0\x07\x38'\ 75 | b'\xf8\x07\x38\xfe\x07\xb8\x3f\x00\xf8\x07\x00\xf8\x01\x00\xf8\x00'\ 76 | b'\x00\x78\x00\x00\x38\x00\x00\x0c\x00\x00\xf0\x00\xe0\xf9\x01\xf0'\ 77 | b'\xff\x03\xf0\x9f\x07\x38\x0e\x07\x38\x0e\x07\x38\x0e\x07\x38\x0e'\ 78 | b'\x07\xf0\x9f\x07\xf0\xff\x03\xe0\xf9\x01\x00\xf0\x00\x0d\x00\xc0'\ 79 | b'\x07\x00\xe0\x0f\x01\xf0\x1f\x03\x78\x3c\x07\x38\x38\x07\x38\x38'\ 80 | b'\x07\x38\x38\x07\x38\x38\x07\x70\x9c\x07\xf0\xff\x03\xe0\xff\x01'\ 81 | b'\x80\x7f\x00\x00\x00\x00\x05\x00\x00\x07\x07\x00\x07\x07\x00\x07'\ 82 | b'\x07\x00\x00\x00\x00\x00\x00\x05\x00\x00\x07\x37\x00\x07\x3f\x00'\ 83 | b'\x07\x1f\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x38\x00\x00\x38\x00'\ 84 | b'\x00\x7c\x00\x00\x6c\x00\x00\xee\x00\x00\xee\x00\x00\xc7\x01\x00'\ 85 | b'\xc7\x01\x80\x83\x03\x00\x00\x00\x0b\x00\x00\xce\x01\x00\xce\x01'\ 86 | b'\x00\xce\x01\x00\xce\x01\x00\xce\x01\x00\xce\x01\x00\xce\x01\x00'\ 87 | b'\xce\x01\x00\xce\x01\x00\xce\x01\x00\x00\x00\x0a\x00\x80\x83\x03'\ 88 | b'\x80\x83\x03\x00\xc7\x01\x00\xc7\x01\x00\xee\x00\x00\xee\x00\x00'\ 89 | b'\x7c\x00\x00\x7c\x00\x00\x38\x00\x00\x00\x00\x0c\x00\xc0\x00\x00'\ 90 | b'\xf0\x00\x00\xf0\x00\x00\x78\x70\x07\x38\x78\x07\x38\x7c\x07\x78'\ 91 | b'\x1e\x00\xf0\x0f\x00\xf0\x07\x00\xc0\x03\x00\x00\x00\x00\x00\x00'\ 92 | b'\x00\x11\x00\x00\x3f\x00\xc0\xff\x00\xe0\xe1\x01\xf0\xc0\x03\x70'\ 93 | b'\xbc\x03\x78\x7e\x07\x38\x7f\x07\xb8\x73\x07\xb8\x33\x07\xb8\x3f'\ 94 | b'\x07\x38\x7f\x07\x70\xf3\x03\x70\x70\x03\xe0\x38\x02\xe0\x3f\x00'\ 95 | b'\x80\x0f\x00\x00\x00\x00\x0f\x00\x00\x00\x06\x00\xc0\x07\x00\xf0'\ 96 | b'\x07\x00\xfe\x03\xc0\xff\x00\xf8\xef\x00\xf8\xe1\x00\x78\xe0\x00'\ 97 | b'\xf8\xe3\x00\xf0\xff\x00\xc0\xff\x00\x00\xfe\x07\x00\xf0\x07\x00'\ 98 | b'\x80\x07\x00\x00\x04\x0f\x00\xf8\xff\x07\xf8\xff\x07\xf8\xff\x07'\ 99 | b'\x38\x0e\x07\x38\x0e\x07\x38\x0e\x07\x38\x0e\x07\x38\x0e\x07\x38'\ 100 | b'\x9e\x07\xf8\xff\x07\xf0\xff\x03\xe0\xf1\x01\x00\x00\x00\x00\x00'\ 101 | b'\x00\x00\x00\x00\x10\x00\x00\x3f\x00\xc0\xff\x00\xe0\xff\x01\xf0'\ 102 | b'\xe1\x03\x70\x80\x03\x78\x80\x07\x38\x00\x07\x38\x00\x07\x38\x00'\ 103 | b'\x07\x38\x00\x07\x78\x80\x03\xf0\xc0\x03\xe0\xc0\x01\xc0\xc0\x00'\ 104 | b'\x80\x40\x00\x00\x00\x00\x0f\x00\xf8\xff\x07\xf8\xff\x07\xf8\xff'\ 105 | b'\x07\x38\x00\x07\x38\x00\x07\x38\x00\x07\x38\x00\x07\x38\x00\x07'\ 106 | b'\x78\x80\x07\xf0\xc0\x03\xe0\xff\x01\xc0\xff\x00\x00\x3f\x00\x00'\ 107 | b'\x00\x00\x00\x00\x00\x0e\x00\xf8\xff\x07\xf8\xff\x07\xf8\xff\x07'\ 108 | b'\x38\x0e\x07\x38\x0e\x07\x38\x0e\x07\x38\x0e\x07\x38\x0e\x07\x38'\ 109 | b'\x0e\x07\x38\x0e\x07\x38\x00\x07\x38\x00\x07\x00\x00\x00\x00\x00'\ 110 | b'\x00\x0d\x00\xf8\xff\x07\xf8\xff\x07\xf8\xff\x07\x38\x1c\x00\x38'\ 111 | b'\x1c\x00\x38\x1c\x00\x38\x1c\x00\x38\x1c\x00\x38\x1c\x00\x38\x1c'\ 112 | b'\x00\x38\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x3f\x00\xc0'\ 113 | b'\xff\x00\xe0\xff\x01\xf0\xe1\x03\x70\x80\x03\x78\x80\x07\x38\x00'\ 114 | b'\x07\x38\x00\x07\x38\x1c\x07\x38\x1c\x07\x78\x9c\x03\xf0\xdc\x01'\ 115 | b'\xe0\xfc\x07\xc0\xfc\x07\x80\xfc\x07\x00\x00\x00\x00\x00\x00\x0f'\ 116 | b'\x00\xf8\xff\x07\xf8\xff\x07\xf8\xff\x07\x00\x0e\x00\x00\x0e\x00'\ 117 | b'\x00\x0e\x00\x00\x0e\x00\x00\x0e\x00\x00\x0e\x00\x00\x0e\x00\xf8'\ 118 | b'\xff\x07\xf8\xff\x07\xf8\xff\x07\x00\x00\x00\x00\x00\x00\x06\x00'\ 119 | b'\xf8\xff\x07\xf8\xff\x07\xf8\xff\x07\x00\x00\x00\x00\x00\x00\x00'\ 120 | b'\x00\x00\x0c\x00\x00\xe0\x00\x00\xe0\x01\x00\xe0\x03\x00\x80\x07'\ 121 | b'\x00\x00\x07\x00\x00\x07\x00\x00\x07\x00\x80\x07\xf8\xff\x03\xf8'\ 122 | b'\xff\x03\xf8\xff\x00\x00\x00\x00\x0e\x00\xf8\xff\x07\xf8\xff\x07'\ 123 | b'\xf8\xff\x07\x00\x1e\x00\x00\x0f\x00\x80\x1f\x00\xc0\x7f\x00\xe0'\ 124 | b'\xf9\x00\xf0\xf0\x03\x78\xc0\x07\x38\x80\x07\x18\x00\x07\x08\x00'\ 125 | b'\x04\x00\x00\x00\x0c\x00\xf8\xff\x07\xf8\xff\x07\xf8\xff\x07\x00'\ 126 | b'\x00\x07\x00\x00\x07\x00\x00\x07\x00\x00\x07\x00\x00\x07\x00\x00'\ 127 | b'\x07\x00\x00\x07\x00\x00\x07\x00\x00\x00\x12\x00\xf8\xff\x07\xf8'\ 128 | b'\xff\x07\xf8\xff\x07\xf8\x01\x00\xf8\x1f\x00\xc0\xff\x00\x00\xfc'\ 129 | b'\x07\x00\xe0\x07\x00\xe0\x07\x00\xfc\x07\xc0\xff\x00\xf8\x1f\x00'\ 130 | b'\xf8\x01\x00\xf8\xff\x07\xf8\xff\x07\xf8\xff\x07\x00\x00\x00\x00'\ 131 | b'\x00\x00\x0f\x00\xf8\xff\x07\xf8\xff\x07\xf8\xff\x07\xf8\x00\x00'\ 132 | b'\xe0\x03\x00\xc0\x0f\x00\x00\x3f\x00\x00\xfc\x00\x00\xf0\x01\x00'\ 133 | b'\xe0\x07\xf8\xff\x07\xf8\xff\x07\xf8\xff\x07\x00\x00\x00\x00\x00'\ 134 | b'\x00\x11\x00\x00\x3f\x00\xc0\xff\x00\xe0\xff\x01\xf0\xe1\x03\x70'\ 135 | b'\x80\x03\x78\x80\x07\x38\x00\x07\x38\x00\x07\x38\x00\x07\x78\x80'\ 136 | b'\x07\x70\x80\x03\xf0\xe1\x03\xe0\xff\x01\xc0\xff\x00\x00\x3f\x00'\ 137 | b'\x00\x00\x00\x00\x00\x00\x0e\x00\xf8\xff\x07\xf8\xff\x07\xf8\xff'\ 138 | b'\x07\x38\x1c\x00\x38\x1c\x00\x38\x1c\x00\x38\x1c\x00\x38\x1c\x00'\ 139 | b'\x78\x1e\x00\xf0\x0f\x00\xf0\x0f\x00\xc0\x03\x00\x00\x00\x00\x00'\ 140 | b'\x00\x00\x11\x00\x00\x3f\x00\xc0\xff\x00\xe0\xff\x01\xf0\xe1\x03'\ 141 | b'\x70\x80\x03\x78\x80\x07\x38\x00\x07\x38\x10\x07\x38\x38\x07\x38'\ 142 | b'\x70\x07\x78\xf0\x07\x70\xe0\x03\xf0\xe1\x03\xe0\xff\x07\xc0\xff'\ 143 | b'\x03\x00\x3f\x02\x00\x00\x00\x0f\x00\xf8\xff\x07\xf8\xff\x07\xf8'\ 144 | b'\xff\x07\x38\x1c\x00\x38\x1c\x00\x38\x1c\x00\x38\x3c\x00\x38\xfc'\ 145 | b'\x00\x38\xfc\x03\x78\xfe\x07\xf0\x9f\x07\xf0\x0f\x07\xc0\x07\x04'\ 146 | b'\x00\x00\x00\x00\x00\x00\x0e\x00\x00\xc0\x00\xe0\xc1\x01\xf0\xe3'\ 147 | b'\x03\xf0\x87\x03\x78\x06\x07\x38\x0e\x07\x38\x0e\x07\x38\x0c\x07'\ 148 | b'\x38\x0c\x07\x78\x9c\x07\xf0\xf8\x03\xe0\xf8\x03\xc0\xf0\x00\x00'\ 149 | b'\x00\x00\x0c\x00\x38\x00\x00\x38\x00\x00\x38\x00\x00\x38\x00\x00'\ 150 | b'\xf8\xff\x07\xf8\xff\x07\xf8\xff\x07\x38\x00\x00\x38\x00\x00\x38'\ 151 | b'\x00\x00\x38\x00\x00\x00\x00\x00\x0e\x00\xf8\xff\x00\xf8\xff\x01'\ 152 | b'\xf8\xff\x03\x00\x80\x07\x00\x00\x07\x00\x00\x07\x00\x00\x07\x00'\ 153 | b'\x00\x07\x00\x80\x07\xf8\xff\x03\xf8\xff\x01\xf8\xff\x00\x00\x00'\ 154 | b'\x00\x00\x00\x00\x0e\x00\x18\x00\x00\xf8\x00\x00\xf8\x07\x00\xf0'\ 155 | b'\x3f\x00\x00\xff\x01\x00\xf8\x07\x00\x80\x07\x00\xe0\x07\x00\xfe'\ 156 | b'\x07\xc0\x7f\x00\xf8\x0f\x00\xf8\x01\x00\x38\x00\x00\x08\x00\x00'\ 157 | b'\x14\x00\x18\x00\x00\xf8\x01\x00\xf8\x1f\x00\xe0\xff\x00\x00\xfe'\ 158 | b'\x07\x00\xe0\x07\x00\xf8\x07\xc0\xff\x07\xf8\x3f\x00\xf8\x03\x00'\ 159 | b'\xf8\x01\x00\xf8\x3f\x00\xc0\xff\x07\x00\xf8\x07\x00\xc0\x07\x00'\ 160 | b'\xfe\x07\xe0\xff\x01\xf8\x1f\x00\xf8\x01\x00\x38\x00\x00\x0e\x00'\ 161 | b'\x08\x00\x04\x18\x00\x07\x78\x80\x07\xf8\xe0\x07\xf0\xf3\x01\xc0'\ 162 | b'\xff\x00\x00\x3f\x00\x80\x7f\x00\xe0\xff\x00\xf0\xf1\x03\xf8\xc0'\ 163 | b'\x07\x38\x80\x07\x18\x00\x06\x08\x00\x04\x0d\x00\x18\x00\x00\x38'\ 164 | b'\x00\x00\xf8\x00\x00\xf0\x03\x00\xc0\x0f\x00\x00\xff\x07\x00\xfc'\ 165 | b'\x07\x00\xff\x07\xc0\x0f\x00\xf0\x03\x00\xf8\x00\x00\x38\x00\x00'\ 166 | b'\x18\x00\x00\x0d\x00\x00\x00\x07\x38\x80\x07\x38\xc0\x07\x38\xe0'\ 167 | b'\x07\x38\x78\x07\x38\x3c\x07\x38\x1e\x07\x38\x0f\x07\xf8\x07\x07'\ 168 | b'\xf8\x01\x07\xf8\x00\x07\x78\x00\x07\x38\x00\x07\x07\x00\xff\xff'\ 169 | b'\x3f\xff\xff\x3f\xff\xff\x3f\x07\x00\x38\x07\x00\x38\x07\x00\x38'\ 170 | b'\x00\x00\x00\x08\x00\x04\x00\x00\x3c\x00\x00\xfc\x01\x00\xfc\x1f'\ 171 | b'\x00\xe0\xff\x00\x00\xfe\x07\x00\xf0\x07\x00\x00\x07\x07\x00\x07'\ 172 | b'\x00\x38\x07\x00\x38\x07\x00\x38\xff\xff\x3f\xff\xff\x3f\xff\xff'\ 173 | b'\x3f\x00\x00\x00\x0c\x00\x00\x18\x00\x00\x1f\x00\xc0\x1f\x00\xf8'\ 174 | b'\x03\x00\xf8\x00\x00\x78\x00\x00\xf8\x03\x00\xe0\x0f\x00\x00\x1f'\ 175 | b'\x00\x00\x1c\x00\x00\x10\x00\x00\x00\x00\x12\x00\x00\x00\x38\x00'\ 176 | b'\x00\x38\x00\x00\x38\x00\x00\x38\x00\x00\x38\x00\x00\x38\x00\x00'\ 177 | b'\x38\x00\x00\x38\x00\x00\x38\x00\x00\x38\x00\x00\x38\x00\x00\x38'\ 178 | b'\x00\x00\x38\x00\x00\x38\x00\x00\x38\x00\x00\x38\x00\x00\x38\x00'\ 179 | b'\x00\x38\x09\x00\x04\x00\x00\x04\x00\x00\x0c\x00\x00\x1c\x00\x00'\ 180 | b'\x18\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0d'\ 181 | b'\x00\x00\xc0\x01\x00\xe4\x03\x00\xe6\x07\x00\x37\x07\x80\x37\x07'\ 182 | b'\x80\x33\x07\x80\x33\x07\x80\x33\x07\x80\x93\x03\x80\xff\x07\x00'\ 183 | b'\xff\x07\x00\xfe\x07\x00\x00\x00\x0e\x00\xfc\xff\x07\xfc\xff\x07'\ 184 | b'\xfc\xff\x07\x00\x87\x03\x80\x03\x07\x80\x03\x07\x80\x03\x07\x80'\ 185 | b'\x03\x07\x80\x87\x07\x00\xff\x03\x00\xfe\x01\x00\xf8\x00\x00\x00'\ 186 | b'\x00\x00\x00\x00\x0d\x00\x00\x78\x00\x00\xfe\x01\x00\xff\x03\x00'\ 187 | b'\x87\x03\x80\x03\x07\x80\x03\x07\x80\x03\x07\x80\x03\x07\x80\x87'\ 188 | b'\x07\x00\x87\x03\x00\x86\x01\x00\x88\x00\x00\x00\x00\x0e\x00\x00'\ 189 | b'\x78\x00\x00\xfe\x01\x00\xff\x03\x00\x87\x03\x80\x87\x07\x80\x03'\ 190 | b'\x07\x80\x03\x07\x80\x03\x07\x80\x87\x07\x00\x87\x03\xfc\xff\x07'\ 191 | b'\xfc\xff\x07\xfc\xff\x07\x00\x00\x00\x0d\x00\x00\x30\x00\x00\xfc'\ 192 | b'\x00\x00\xfe\x01\x00\xff\x03\x80\x77\x07\x80\x73\x07\x80\x73\x07'\ 193 | b'\x80\x73\x07\x80\xf7\x07\x00\xff\x03\x00\x7e\x03\x00\x7c\x00\x00'\ 194 | b'\x60\x00\x07\x00\x80\x03\x00\xf0\xff\x07\xfc\xff\x07\xfc\xff\x07'\ 195 | b'\x9e\x03\x00\x8e\x03\x00\x00\x00\x00\x0e\x00\x00\x78\x00\x00\xfe'\ 196 | b'\x01\x00\xff\x23\x00\x87\x73\x80\x87\xf7\x80\x03\xe7\x80\x03\xe7'\ 197 | b'\x80\x03\xe7\x00\x87\xe7\x00\x87\xf3\x80\xff\x7f\x80\xff\x3f\x80'\ 198 | b'\xff\x1f\x00\x00\x00\x0d\x00\xfc\xff\x07\xfc\xff\x07\xfc\xff\x07'\ 199 | b'\x00\x07\x00\x80\x03\x00\x80\x03\x00\x80\x03\x00\x80\x07\x00\x00'\ 200 | b'\xff\x07\x00\xff\x07\x00\xfc\x07\x00\x00\x00\x00\x00\x00\x05\x00'\ 201 | b'\xb8\xff\x07\xb8\xff\x07\xb8\xff\x07\x00\x00\x00\x00\x00\x00\x05'\ 202 | b'\x00\x00\x00\xe0\x00\x00\xe0\xb8\xff\xff\xb8\xff\xff\xb8\xff\x7f'\ 203 | b'\x0c\x00\xfc\xff\x07\xfc\xff\x07\xfc\xff\x07\x00\x78\x00\x00\x3c'\ 204 | b'\x00\x00\xfe\x00\x80\xff\x01\x80\xe7\x07\x80\x83\x07\x80\x01\x07'\ 205 | b'\x80\x00\x04\x00\x00\x00\x05\x00\xfc\xff\x07\xfc\xff\x07\xfc\xff'\ 206 | b'\x07\x00\x00\x00\x00\x00\x00\x14\x00\x80\xff\x07\x80\xff\x07\x80'\ 207 | b'\xff\x07\x00\x07\x00\x80\x03\x00\x80\x03\x00\x80\x03\x00\x80\x07'\ 208 | b'\x00\x00\xff\x07\x00\xfe\x07\x00\xff\x07\x80\x07\x00\x80\x03\x00'\ 209 | b'\x80\x03\x00\x80\x07\x00\x00\xff\x07\x00\xff\x07\x00\xfc\x07\x00'\ 210 | b'\x00\x00\x00\x00\x00\x0d\x00\x80\xff\x07\x80\xff\x07\x80\xff\x07'\ 211 | b'\x00\x07\x00\x80\x03\x00\x80\x03\x00\x80\x03\x00\x80\x07\x00\x00'\ 212 | b'\xff\x07\x00\xff\x07\x00\xfc\x07\x00\x00\x00\x00\x00\x00\x0d\x00'\ 213 | b'\x00\x78\x00\x00\xfe\x01\x00\xff\x03\x00\x87\x03\x80\x03\x07\x80'\ 214 | b'\x03\x07\x80\x03\x07\x80\x03\x07\x00\x87\x03\x00\xff\x03\x00\xfe'\ 215 | b'\x01\x00\x78\x00\x00\x00\x00\x0e\x00\x80\xff\xff\x80\xff\xff\x80'\ 216 | b'\xff\xff\x00\x87\x03\x80\x03\x07\x80\x03\x07\x80\x03\x07\x80\x03'\ 217 | b'\x07\x80\x87\x07\x00\xff\x03\x00\xfe\x01\x00\x7c\x00\x00\x00\x00'\ 218 | b'\x00\x00\x00\x0e\x00\x00\x78\x00\x00\xfe\x01\x00\xff\x03\x00\x87'\ 219 | b'\x03\x80\x87\x07\x80\x03\x07\x80\x03\x07\x80\x03\x07\x80\x87\x07'\ 220 | b'\x00\x87\x03\x80\xff\xff\x80\xff\xff\x80\xff\xff\x00\x00\x00\x08'\ 221 | b'\x00\x80\xff\x07\x80\xff\x07\x80\xff\x07\x00\x07\x00\x80\x03\x00'\ 222 | b'\x80\x03\x00\x80\x03\x00\x00\x00\x00\x0c\x00\x00\x80\x00\x00\x8e'\ 223 | b'\x01\x00\x9f\x03\x80\x9f\x07\x80\x3b\x07\x80\x33\x07\x80\x33\x07'\ 224 | b'\x80\x33\x07\x00\xf7\x07\x00\xe7\x03\x00\xc6\x01\x00\x00\x00\x07'\ 225 | b'\x00\x80\x03\x00\xfc\xff\x00\xfc\xff\x03\xfc\xff\x07\x80\x03\x07'\ 226 | b'\x80\x03\x07\x00\x00\x00\x0d\x00\x80\xff\x00\x80\xff\x03\x80\xff'\ 227 | b'\x03\x00\x80\x07\x00\x00\x07\x00\x00\x07\x00\x00\x07\x00\x80\x03'\ 228 | b'\x80\xff\x07\x80\xff\x07\x80\xff\x07\x00\x00\x00\x00\x00\x00\x0c'\ 229 | b'\x00\x80\x01\x00\x80\x0f\x00\x80\x3f\x00\x00\xfe\x01\x00\xf0\x07'\ 230 | b'\x00\x80\x07\x00\xc0\x07\x00\xf8\x07\x00\xff\x00\x80\x3f\x00\x80'\ 231 | b'\x07\x00\x80\x00\x00\x12\x00\x80\x01\x00\x80\x0f\x00\x80\xff\x00'\ 232 | b'\x00\xfe\x07\x00\xe0\x07\x00\xc0\x07\x00\xfc\x07\x80\xff\x00\x80'\ 233 | b'\x0f\x00\x80\x3f\x00\x00\xff\x01\x00\xf0\x07\x00\x80\x07\x00\xf8'\ 234 | b'\x07\x80\xff\x03\x80\x3f\x00\x80\x07\x00\x80\x00\x00\x0c\x00\x80'\ 235 | b'\x00\x04\x80\x01\x07\x80\x87\x07\x80\xef\x07\x00\xfe\x01\x00\x7c'\ 236 | b'\x00\x00\xfe\x00\x00\xff\x03\x80\xc7\x07\x80\x03\x07\x80\x00\x06'\ 237 | b'\x00\x00\x04\x0c\x00\x80\x01\x00\x80\x07\xe0\x80\x3f\xe0\x00\xff'\ 238 | b'\xf0\x00\xf8\xff\x00\xc0\x7f\x00\xc0\x1f\x00\xf8\x07\x00\xff\x00'\ 239 | b'\x80\x1f\x00\x80\x07\x00\x80\x00\x00\x0b\x00\x80\x03\x07\x80\x83'\ 240 | b'\x07\x80\xc3\x07\x80\xe3\x07\x80\xf3\x07\x80\x7b\x07\x80\x3f\x07'\ 241 | b'\x80\x1f\x07\x80\x0f\x07\x80\x07\x07\x00\x00\x07\x08\x00\x00\x0c'\ 242 | b'\x00\x00\x1e\x00\xfc\xff\x0f\xfe\xff\x1f\xfe\xf3\x1f\x06\x00\x38'\ 243 | b'\x06\x00\x38\x00\x00\x00\x05\x00\xfc\xff\x07\xfc\xff\x07\xfc\xff'\ 244 | b'\x07\x00\x00\x00\x00\x00\x00\x07\x00\x06\x00\x38\xfe\xf3\x1f\xfe'\ 245 | b'\xff\x1f\xfc\xff\x0f\x00\x1e\x00\x00\x0c\x00\x00\x00\x00\x09\x00'\ 246 | b'\x18\x00\x00\x0c\x00\x00\x0c\x00\x00\x0c\x00\x00\x18\x00\x00\x18'\ 247 | b'\x00\x00\x1c\x00\x00\x04\x00\x00\x00\x00\x00' 248 | 249 | _index =\ 250 | b'\x00\x00\x26\x00\x3d\x00\x51\x00\x6b\x00\x91\x00\xb4\x00\xec\x00'\ 251 | b'\x1b\x01\x29\x01\x40\x01\x57\x01\x74\x01\x9a\x01\xab\x01\xc2\x01'\ 252 | b'\xd3\x01\xed\x01\x16\x02\x30\x02\x59\x02\x7f\x02\xa5\x02\xcb\x02'\ 253 | b'\xf4\x02\x17\x03\x3d\x03\x66\x03\x77\x03\x88\x03\xa8\x03\xcb\x03'\ 254 | b'\xeb\x03\x11\x04\x46\x04\x75\x04\xa4\x04\xd6\x04\x05\x05\x31\x05'\ 255 | b'\x5a\x05\x8f\x05\xbe\x05\xd2\x05\xf8\x05\x24\x06\x4a\x06\x82\x06'\ 256 | b'\xb1\x06\xe6\x06\x12\x07\x47\x07\x76\x07\xa2\x07\xc8\x07\xf4\x07'\ 257 | b'\x20\x08\x5e\x08\x8a\x08\xb3\x08\xdc\x08\xf3\x08\x0d\x09\x24\x09'\ 258 | b'\x4a\x09\x82\x09\x9f\x09\xc8\x09\xf4\x09\x1d\x0a\x49\x0a\x72\x0a'\ 259 | b'\x89\x0a\xb5\x0a\xde\x0a\xef\x0a\x00\x0b\x26\x0b\x37\x0b\x75\x0b'\ 260 | b'\x9e\x0b\xc7\x0b\xf3\x0b\x1f\x0c\x39\x0c\x5f\x0c\x76\x0c\x9f\x0c'\ 261 | b'\xc5\x0c\xfd\x0c\x23\x0d\x49\x0d\x6c\x0d\x86\x0d\x97\x0d\xae\x0d'\ 262 | b'\xcb\x0d' 263 | 264 | _mvfont = memoryview(_font) 265 | 266 | def _chr_addr(ordch): 267 | offset = 2 * (ordch - 32) 268 | return int.from_bytes(_index[offset:offset + 2], 'little') 269 | 270 | def get_width(s): 271 | width = 0 272 | for ch in s: 273 | ordch = ord(ch) 274 | ordch = ordch + 1 if ordch >= 32 and ordch <= 126 else 32 275 | offset = _chr_addr(ordch) 276 | width += int.from_bytes(_font[offset:offset + 2], 'little') 277 | return width 278 | 279 | def get_ch(ch): 280 | ordch = ord(ch) 281 | ordch = ordch + 1 if ordch >= 32 and ordch <= 126 else 32 282 | offset = _chr_addr(ordch) 283 | width = int.from_bytes(_font[offset:offset + 2], 'little') 284 | next_offs = _chr_addr(ordch +1) 285 | return _mvfont[offset + 2:next_offs], width 286 | 287 | -------------------------------------------------------------------------------- /fonts/tt32.py: -------------------------------------------------------------------------------- 1 | # Code generated by font-to-py.py. 2 | # Font: CM Sans Serif 2012.ttf 3 | version = '0.2' 4 | 5 | def height(): 6 | return 31 7 | 8 | def max_width(): 9 | return 26 10 | 11 | def hmap(): 12 | return False 13 | 14 | def reverse(): 15 | return False 16 | 17 | def monospaced(): 18 | return False 19 | 20 | def min_ch(): 21 | return 32 22 | 23 | def max_ch(): 24 | return 126 25 | 26 | _font =\ 27 | b'\x0f\x00\x00\x07\x00\x00\xc0\x07\x00\x00\xe0\x07\x00\x00\xe0\x07'\ 28 | b'\x00\x00\xf0\x01\x67\x00\xf0\xc0\xf7\x00\xf0\xe0\xf7\x00\xf0\xe0'\ 29 | b'\x67\x00\xf0\xf1\x01\x00\xe0\xff\x00\x00\xe0\x7f\x00\x00\xc0\x3f'\ 30 | b'\x00\x00\x00\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00'\ 31 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 32 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 33 | b'\x00\x00\x00\x00\x08\x00\xf0\xff\xf3\x00\xf0\xff\xf3\x00\xf0\xff'\ 34 | b'\xf3\x00\xf0\xff\x63\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 35 | b'\x00\x00\x00\x00\x00\x00\x0a\x00\xf0\x0f\x00\x00\xf0\x0f\x00\x00'\ 36 | b'\xf0\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x0f\x00\x00'\ 37 | b'\xf0\x0f\x00\x00\xf0\x0f\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00'\ 38 | b'\x0f\x00\x00\x00\x0e\x00\x00\x0e\x0e\x00\x00\x0e\x7e\x00\x00\xfe'\ 39 | b'\x7f\x00\xe0\xff\x7f\x00\xe0\xff\x0f\x00\xe0\x0f\x0e\x00\x00\x0e'\ 40 | b'\x0e\x00\x00\x0e\x7f\x00\x00\xfe\x7f\x00\xe0\xff\x7f\x00\xe0\xff'\ 41 | b'\x0f\x00\xe0\x0f\x0e\x00\x00\x0e\x0e\x00\x00\x0e\x00\x00\x0e\x00'\ 42 | b'\x00\x1f\x0e\x00\x80\x3f\x1e\x00\xc0\x3f\x3e\x00\xc0\x79\x3c\x00'\ 43 | b'\xc0\x71\x38\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\xc0\xe1\x38\x00'\ 44 | b'\xc0\xe3\x38\x00\xc0\xe7\x3f\x00\x80\xc7\x1f\x00\x00\x86\x0f\x00'\ 45 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x17\x00\x80\x07\x00\x00\xc0\x0f'\ 46 | b'\x00\x00\xe0\x1f\x00\x00\xf0\x3c\x00\x00\x70\x38\x80\x00\x70\x38'\ 47 | b'\xe0\x00\x70\x38\xf8\x00\xf0\x3c\x7e\x00\xe0\x1f\x1f\x00\xc0\xcf'\ 48 | b'\x0f\x00\x80\xf7\x03\x00\x00\xfc\x00\x00\x00\x3f\x1e\x00\x80\x8f'\ 49 | b'\x7f\x00\xe0\x87\x7f\x00\xf0\xc1\xf3\x00\x70\xc0\xe1\x00\x10\xc0'\ 50 | b'\xe1\x00\x00\xc0\xf3\x00\x00\x80\x7f\x00\x00\x80\x7f\x00\x00\x00'\ 51 | b'\x1e\x00\x00\x00\x00\x00\x13\x00\x00\x00\x0f\x00\x00\x80\x3f\x00'\ 52 | b'\x00\xc0\x3f\x00\x80\xe7\x7f\x00\xe0\x7f\xf8\x00\xe0\x3f\xf8\x00'\ 53 | b'\xf0\x3f\xf0\x00\xf0\x78\xf0\x00\xf0\xf8\xf0\x00\xf0\xff\xf3\x00'\ 54 | b'\xe0\xdf\xff\x00\xe0\x8f\xff\x00\x80\x07\x7e\x00\x00\xc0\xff\x00'\ 55 | b'\x00\xc0\xff\x00\x00\xc0\xff\x00\x00\xc0\xc3\x00\x00\x00\x80\x00'\ 56 | b'\x00\x00\x00\x00\x05\x00\xf0\x0f\x00\x00\xf0\x0f\x00\x00\xf0\x0f'\ 57 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\xfc\x03\x00'\ 58 | b'\x80\xff\x1f\x00\xe0\xff\x7f\x00\xf8\xff\xff\x01\xfc\x01\xf8\x03'\ 59 | b'\x3e\x00\xc0\x07\x0e\x00\x00\x07\x02\x00\x00\x04\x00\x00\x00\x00'\ 60 | b'\x09\x00\x02\x00\x00\x04\x0e\x00\x00\x07\x3e\x00\xc0\x07\xfc\x01'\ 61 | b'\xf8\x03\xf8\xff\xff\x01\xe0\xff\x7f\x00\x80\xff\x1f\x00\x00\xfc'\ 62 | b'\x03\x00\x00\x00\x00\x00\x0b\x00\x80\x01\x00\x00\xa0\x09\x00\x00'\ 63 | b'\xf0\x1f\x00\x00\xe0\x0f\x00\x00\xc0\x07\x00\x00\xe0\x0f\x00\x00'\ 64 | b'\xf0\x1d\x00\x00\xa0\x09\x00\x00\x80\x01\x00\x00\x00\x00\x00\x00'\ 65 | b'\x00\x00\x00\x00\x0f\x00\x00\xc0\x03\x00\x00\xc0\x03\x00\x00\xc0'\ 66 | b'\x03\x00\x00\xc0\x03\x00\x00\xc0\x03\x00\x00\xfe\x7f\x00\x00\xfe'\ 67 | b'\x7f\x00\x00\xfe\x7f\x00\x00\xfe\x7f\x00\x00\xc0\x03\x00\x00\xc0'\ 68 | b'\x03\x00\x00\xc0\x03\x00\x00\xc0\x03\x00\x00\xc0\x03\x00\x00\x00'\ 69 | b'\x00\x00\x06\x00\x00\x00\x60\x0c\x00\x00\xf0\x0c\x00\x00\xf0\x07'\ 70 | b'\x00\x00\xe0\x03\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\xc0'\ 71 | b'\x03\x00\x00\xc0\x03\x00\x00\xc0\x03\x00\x00\xc0\x03\x00\x00\xc0'\ 72 | b'\x03\x00\x00\xc0\x03\x00\x00\xc0\x03\x00\x00\x00\x00\x00\x00\x00'\ 73 | b'\x00\x00\x06\x00\x00\x00\x60\x00\x00\x00\xf0\x00\x00\x00\xf0\x00'\ 74 | b'\x00\x00\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00'\ 75 | b'\xe0\x00\x00\x00\xfc\x00\x00\xc0\xff\x00\x00\xf8\xff\x00\x80\xff'\ 76 | b'\x1f\x00\xf8\xff\x03\x00\xfc\x3f\x00\x00\xfc\x07\x00\x00\x7c\x00'\ 77 | b'\x00\x00\x04\x00\x00\x00\x11\x00\x00\xfc\x03\x00\x80\xff\x1f\x00'\ 78 | b'\xc0\xff\x3f\x00\xe0\xff\x7f\x00\xe0\x07\x7e\x00\xf0\x01\xf8\x00'\ 79 | b'\xf0\x00\xf0\x00\xf0\x00\xf0\x00\xf0\x00\xf0\x00\xf0\x01\xf8\x00'\ 80 | b'\xe0\x07\x7e\x00\xe0\xff\x7f\x00\xc0\xff\x3f\x00\x80\xff\x1f\x00'\ 81 | b'\x00\xfc\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x80\x03'\ 82 | b'\x00\x00\x80\x03\x00\x00\xc0\x03\x00\x00\xe0\xff\xff\x00\xf0\xff'\ 83 | b'\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\x00\x00\x00\x00\x00\x00'\ 84 | b'\x00\x00\x10\x00\x00\x07\xf8\x00\xc0\x07\xfc\x00\xc0\x07\xfe\x00'\ 85 | b'\xe0\x07\xff\x00\xf0\x81\xf7\x00\xf0\x80\xf3\x00\xf0\xc0\xf1\x00'\ 86 | b'\xf0\xe0\xf1\x00\xf0\xe0\xf0\x00\xf0\xf1\xf0\x00\xe0\x7f\xf0\x00'\ 87 | b'\xe0\x7f\xf0\x00\xc0\x3f\xf0\x00\x00\x0f\xf0\x00\x00\x00\x00\x00'\ 88 | b'\x00\x00\x00\x00\x10\x00\x00\x00\x0e\x00\x00\x07\x3e\x00\xc0\x07'\ 89 | b'\x3e\x00\xe0\x07\x7e\x00\xe0\x07\xf8\x00\xf0\x01\xf0\x00\xf0\xf0'\ 90 | b'\xf0\x00\xf0\xf0\xf0\x00\xf0\xf0\xf0\x00\xf0\xf9\xf0\x00\xf0\xff'\ 91 | b'\xf9\x00\xe0\xff\x7f\x00\xc0\xdf\x7f\x00\x80\x8f\x3f\x00\x00\x00'\ 92 | b'\x0f\x00\x00\x00\x00\x00\x10\x00\x00\x00\x0f\x00\x00\x80\x0f\x00'\ 93 | b'\x00\xe0\x0f\x00\x00\xf8\x0f\x00\x00\x7c\x0f\x00\x00\x3f\x0f\x00'\ 94 | b'\x80\x0f\x0f\x00\xe0\x07\x0f\x00\xf0\x01\x0f\x00\xf0\xff\xff\x00'\ 95 | b'\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\x00\x00\x0f\x00'\ 96 | b'\x00\x00\x0f\x00\x00\x00\x00\x00\x0f\x00\x00\x70\x1c\x00\xf0\x7f'\ 97 | b'\x3c\x00\xf0\x7f\x7c\x00\xf0\xff\x7c\x00\xf0\x78\xf8\x00\xf0\x3c'\ 98 | b'\xf0\x00\xf0\x3c\xf0\x00\xf0\x3c\xf0\x00\xf0\x3c\xf0\x00\xf0\x7c'\ 99 | b'\x78\x00\xf0\xf8\x7f\x00\xf0\xf0\x3f\x00\xf0\xe0\x1f\x00\x00\xc0'\ 100 | b'\x0f\x00\x00\x00\x00\x00\x10\x00\x00\xfc\x07\x00\x00\xff\x1f\x00'\ 101 | b'\xc0\xff\x3f\x00\xe0\xff\x7f\x00\xe0\xf3\x78\x00\xf0\x79\xf0\x00'\ 102 | b'\xf0\x78\xf0\x00\xf0\x78\xf0\x00\xf0\x78\xf0\x00\xf0\xf9\xf8\x00'\ 103 | b'\xe0\xf3\x7f\x00\xe0\xf3\x7f\x00\xc0\xe3\x3f\x00\x00\x83\x0f\x00'\ 104 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\xf0\x00\x00\x00\xf0\x00'\ 105 | b'\x00\x00\xf0\x00\x00\x00\xf0\x00\xf8\x00\xf0\x80\xff\x00\xf0\xe0'\ 106 | b'\xff\x00\xf0\xf8\xff\x00\xf0\xfc\x07\x00\xf0\x7e\x00\x00\xf0\x1f'\ 107 | b'\x00\x00\xf0\x07\x00\x00\xf0\x03\x00\x00\xf0\x01\x00\x00\xf0\x00'\ 108 | b'\x00\x00\x10\x00\x00\x00\x0f\x00\x80\xc7\x3f\x00\xc0\xef\x7f\x00'\ 109 | b'\xe0\xff\x7f\x00\xe0\xff\xf8\x00\xf0\x7d\xf0\x00\xf0\x78\xf0\x00'\ 110 | b'\xf0\x78\xf0\x00\xf0\x78\xf0\x00\xf0\x7d\xf0\x00\xe0\xff\xf8\x00'\ 111 | b'\xe0\xff\x7f\x00\xc0\xef\x7f\x00\x80\xc7\x3f\x00\x00\x00\x0f\x00'\ 112 | b'\x00\x00\x00\x00\x10\x00\x00\x3f\x00\x00\x80\xff\x30\x00\xc0\xff'\ 113 | b'\x71\x00\xe0\xff\x71\x00\xf0\xe1\xf3\x00\xf0\xc1\xf3\x00\xf0\xc0'\ 114 | b'\xf3\x00\xf0\xc0\xf3\x00\xf0\xc0\xf3\x00\xf0\xc1\xf9\x00\xe0\xe1'\ 115 | b'\x7d\x00\xe0\xff\x7f\x00\xc0\xff\x3f\x00\x80\xff\x1f\x00\x00\xfc'\ 116 | b'\x03\x00\x00\x00\x00\x00\x06\x00\x00\x18\x60\x00\x00\x3c\xf0\x00'\ 117 | b'\x00\x3c\xf0\x00\x00\x18\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 118 | b'\x06\x00\x00\x18\x60\x0c\x00\x3c\xf0\x0c\x00\x3c\xf0\x07\x00\x18'\ 119 | b'\xe0\x03\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\xc0\x03\x00'\ 120 | b'\x00\xe0\x07\x00\x00\xe0\x07\x00\x00\xf0\x0f\x00\x00\xf0\x0f\x00'\ 121 | b'\x00\x78\x1e\x00\x00\x78\x1e\x00\x00\x38\x1c\x00\x00\x3c\x3c\x00'\ 122 | b'\x00\x1c\x38\x00\x00\x1e\x78\x00\x00\x00\x00\x00\x0e\x00\x00\x78'\ 123 | b'\x1e\x00\x00\x78\x1e\x00\x00\x78\x1e\x00\x00\x78\x1e\x00\x00\x78'\ 124 | b'\x1e\x00\x00\x78\x1e\x00\x00\x78\x1e\x00\x00\x78\x1e\x00\x00\x78'\ 125 | b'\x1e\x00\x00\x78\x1e\x00\x00\x78\x1e\x00\x00\x78\x1e\x00\x00\x00'\ 126 | b'\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x1e\x78\x00\x00\x3c\x3c\x00'\ 127 | b'\x00\x3c\x3c\x00\x00\x78\x1e\x00\x00\x78\x1e\x00\x00\x70\x0e\x00'\ 128 | b'\x00\xf0\x0f\x00\x00\xe0\x07\x00\x00\xe0\x07\x00\x00\xc0\x07\x00'\ 129 | b'\x00\xc0\x03\x00\x00\x00\x00\x00\x0f\x00\x00\x07\x00\x00\xc0\x07'\ 130 | b'\x00\x00\xe0\x07\x00\x00\xe0\x07\x00\x00\xf0\x01\x67\x00\xf0\xc0'\ 131 | b'\xf7\x00\xf0\xe0\xf7\x00\xf0\xe0\x67\x00\xf0\xf1\x01\x00\xe0\xff'\ 132 | b'\x00\x00\xe0\x7f\x00\x00\xc0\x3f\x00\x00\x00\x1f\x00\x00\x00\x00'\ 133 | b'\x00\x00\x00\x00\x00\x00\x16\x00\x00\xf8\x01\x00\x00\xfe\x07\x00'\ 134 | b'\x80\xff\x1f\x00\xc0\x0f\x1e\x00\xc0\x03\x3c\x00\xe0\xe1\x79\x00'\ 135 | b'\xe0\xf8\x73\x00\xf0\xfc\xf7\x00\x70\x1c\xe7\x00\x70\x0e\xe7\x00'\ 136 | b'\x70\x8e\xe3\x00\x70\xfe\xe1\x00\x70\xfc\xe3\x00\xf0\xfc\xe7\x00'\ 137 | b'\xe0\x0c\x77\x00\xe0\x01\x77\x00\xc0\xc3\x23\x00\xc0\xff\x43\x00'\ 138 | b'\x00\xff\x01\x00\x00\x7e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 139 | b'\x13\x00\x00\x00\xc0\x00\x00\x00\xf8\x00\x00\x00\xfe\x00\x00\xc0'\ 140 | b'\xff\x00\x00\xf8\x3f\x00\x00\xff\x0f\x00\xe0\xff\x0f\x00\xf0\x1f'\ 141 | b'\x0f\x00\xf0\x03\x0f\x00\xf0\x00\x0f\x00\xf0\x07\x0f\x00\xf0\x3f'\ 142 | b'\x0f\x00\xc0\xff\x0f\x00\x00\xfe\x0f\x00\x00\xf0\x7f\x00\x00\x80'\ 143 | b'\xff\x00\x00\x00\xfc\x00\x00\x00\xf0\x00\x00\x00\x80\x00\x12\x00'\ 144 | b'\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00'\ 145 | b'\xf0\xf0\xf0\x00\xf0\xf0\xf0\x00\xf0\xf0\xf0\x00\xf0\xf0\xf0\x00'\ 146 | b'\xf0\xf0\xf0\x00\xf0\xf0\xf0\x00\xf0\xf0\xf0\x00\xf0\xf9\xf0\x00'\ 147 | b'\xf0\xff\xf9\x00\xe0\xff\x7f\x00\xc0\xdf\x7f\x00\x80\x8f\x3f\x00'\ 148 | b'\x00\x00\x1f\x00\x00\x00\x00\x00\x14\x00\x00\xf8\x01\x00\x00\xfe'\ 149 | b'\x07\x00\x80\xff\x1f\x00\xc0\xff\x3f\x00\xc0\x0f\x3f\x00\xe0\x03'\ 150 | b'\x7c\x00\xe0\x01\x78\x00\xf0\x01\xf8\x00\xf0\x00\xf0\x00\xf0\x00'\ 151 | b'\xf0\x00\xf0\x00\xf0\x00\xf0\x00\xf0\x00\xf0\x01\xf8\x00\xe0\x01'\ 152 | b'\x78\x00\xe0\x07\x7e\x00\xc0\x07\x3e\x00\x80\x07\x1e\x00\x00\x03'\ 153 | b'\x0c\x00\x00\x04\x04\x00\x00\x00\x00\x00\x13\x00\xf0\xff\xff\x00'\ 154 | b'\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\x00\xf0\x00'\ 155 | b'\xf0\x00\xf0\x00\xf0\x00\xf0\x00\xf0\x00\xf0\x00\xf0\x00\xf0\x00'\ 156 | b'\xf0\x00\xf8\x00\xe0\x01\x78\x00\xe0\x07\x7e\x00\xc0\xff\x3f\x00'\ 157 | b'\x80\xff\x1f\x00\x00\xff\x0f\x00\x00\xfc\x03\x00\x00\x00\x00\x00'\ 158 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\xf0\xff\xff\x00\xf0\xff'\ 159 | b'\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xf0\xf0\x00\xf0\xf0'\ 160 | b'\xf0\x00\xf0\xf0\xf0\x00\xf0\xf0\xf0\x00\xf0\xf0\xf0\x00\xf0\xf0'\ 161 | b'\xf0\x00\xf0\xf0\xf0\x00\xf0\xf0\xf0\x00\xf0\xf0\xf0\x00\xf0\x00'\ 162 | b'\xf0\x00\xf0\x00\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00'\ 163 | b'\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00'\ 164 | b'\xf0\xf0\x00\x00\xf0\xf0\x00\x00\xf0\xf0\x00\x00\xf0\xf0\x00\x00'\ 165 | b'\xf0\xf0\x00\x00\xf0\xf0\x00\x00\xf0\xf0\x00\x00\xf0\xf0\x00\x00'\ 166 | b'\xf0\xf0\x00\x00\xf0\x00\x00\x00\xf0\x00\x00\x00\x00\x00\x00\x00'\ 167 | b'\x15\x00\x00\xf8\x01\x00\x00\xfe\x0f\x00\x80\xff\x1f\x00\xc0\xff'\ 168 | b'\x3f\x00\xc0\x0f\x7f\x00\xe0\x03\x7c\x00\xe0\x01\xf8\x00\xf0\x01'\ 169 | b'\xf0\x00\xf0\x00\xf0\x00\xf0\x00\xf0\x00\xf0\xe0\xf1\x00\xf0\xe0'\ 170 | b'\xf1\x00\xf0\xe1\x79\x00\xe0\xe1\x79\x00\xe0\xe3\x3d\x00\xc0\xe7'\ 171 | b'\xff\x00\x80\xe7\xff\x00\x00\xe3\xff\x00\x00\xe4\xff\x00\x00\x00'\ 172 | b'\x00\x00\x00\x00\x00\x00\x14\x00\xf0\xff\xff\x00\xf0\xff\xff\x00'\ 173 | b'\xf0\xff\xff\x00\xf0\xff\xff\x00\x00\xf0\x00\x00\x00\xf0\x00\x00'\ 174 | b'\x00\xf0\x00\x00\x00\xf0\x00\x00\x00\xf0\x00\x00\x00\xf0\x00\x00'\ 175 | b'\x00\xf0\x00\x00\x00\xf0\x00\x00\xf0\xff\xff\x00\xf0\xff\xff\x00'\ 176 | b'\xf0\xff\xff\x00\xf0\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 177 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00\xf0\xff\xff\x00\xf0\xff'\ 178 | b'\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\x00\x00\x00\x00\x00\x00'\ 179 | b'\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x0f\x00\x00\x00\x3f\x00'\ 180 | b'\x00\x00\x7f\x00\x00\x00\x7f\x00\x00\x00\xf8\x00\x00\x00\xf0\x00'\ 181 | b'\x00\x00\xf0\x00\x00\x00\xf0\x00\x00\x00\xf0\x00\x00\x00\xf8\x00'\ 182 | b'\xf0\xff\x7f\x00\xf0\xff\x7f\x00\xf0\xff\x3f\x00\xf0\xff\x0f\x00'\ 183 | b'\x00\x00\x00\x00\x12\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff'\ 184 | b'\xff\x00\xf0\xff\xff\x00\x00\xf0\x01\x00\x00\xfc\x00\x00\x00\xfe'\ 185 | b'\x00\x00\x00\xff\x01\x00\x80\xff\x07\x00\xc0\xef\x0f\x00\xe0\x87'\ 186 | b'\x3f\x00\xf0\x01\x7f\x00\xf0\x00\xfc\x00\x70\x00\xf8\x00\x30\x00'\ 187 | b'\xf0\x00\x10\x00\xc0\x00\x10\x00\x80\x00\x00\x00\x00\x00\x10\x00'\ 188 | b'\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00'\ 189 | b'\x00\x00\xf0\x00\x00\x00\xf0\x00\x00\x00\xf0\x00\x00\x00\xf0\x00'\ 190 | b'\x00\x00\xf0\x00\x00\x00\xf0\x00\x00\x00\xf0\x00\x00\x00\xf0\x00'\ 191 | b'\x00\x00\xf0\x00\x00\x00\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 192 | b'\x18\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff'\ 193 | b'\xff\x00\xf0\x07\x00\x00\xf0\x7f\x00\x00\x80\xff\x07\x00\x00\xfc'\ 194 | b'\x3f\x00\x00\xc0\xff\x00\x00\x00\xfe\x00\x00\x00\xfe\x00\x00\xc0'\ 195 | b'\xff\x00\x00\xfc\x3f\x00\x80\xff\x07\x00\xf0\x7f\x00\x00\xf0\x07'\ 196 | b'\x00\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff'\ 197 | b'\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 198 | b'\x00\x00\x14\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00'\ 199 | b'\xf0\xff\xff\x00\xf0\x07\x00\x00\xc0\x1f\x00\x00\x00\x3f\x00\x00'\ 200 | b'\x00\xfc\x00\x00\x00\xf0\x03\x00\x00\xc0\x0f\x00\x00\x80\x3f\x00'\ 201 | b'\x00\x00\xfe\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00'\ 202 | b'\xf0\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 203 | b'\x00\x00\x00\x00\x16\x00\x00\xf8\x01\x00\x00\xfe\x07\x00\x80\xff'\ 204 | b'\x1f\x00\xc0\xff\x3f\x00\xc0\x0f\x3f\x00\xe0\x03\x7c\x00\xe0\x01'\ 205 | b'\x78\x00\xf0\x01\xf8\x00\xf0\x00\xf0\x00\xf0\x00\xf0\x00\xf0\x00'\ 206 | b'\xf0\x00\xf0\x00\xf0\x00\xf0\x01\xf8\x00\xe0\x01\x78\x00\xe0\x03'\ 207 | b'\x7c\x00\xc0\x0f\x3f\x00\xc0\xff\x3f\x00\x80\xff\x1f\x00\x00\xfe'\ 208 | b'\x07\x00\x00\xf8\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00'\ 209 | b'\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00'\ 210 | b'\xf0\xe0\x01\x00\xf0\xe0\x01\x00\xf0\xe0\x01\x00\xf0\xe0\x01\x00'\ 211 | b'\xf0\xe0\x01\x00\xf0\xe0\x01\x00\xf0\xf1\x01\x00\xe0\xff\x00\x00'\ 212 | b'\xe0\xff\x00\x00\xc0\x7f\x00\x00\x00\x1f\x00\x00\x00\x00\x00\x00'\ 213 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\xf8\x01\x00\x00\xff'\ 214 | b'\x0f\x00\x80\xff\x1f\x00\xc0\xff\x3f\x00\xe0\x0f\x7e\x00\xe0\x03'\ 215 | b'\x7c\x00\xf0\x01\xf8\x00\xf0\x00\xf0\x00\xf0\x00\xf1\x00\xf0\x80'\ 216 | b'\xf3\x00\xf0\xc0\xf7\x00\xf0\x80\xff\x00\xf0\x01\xff\x00\xe0\x03'\ 217 | b'\x7e\x00\xe0\x0f\x7e\x00\xc0\xff\x7f\x00\x80\xff\xff\x00\x00\xfe'\ 218 | b'\x77\x00\x00\xf8\x21\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00'\ 219 | b'\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\xff\xff\x00'\ 220 | b'\xf0\xe0\x01\x00\xf0\xe0\x01\x00\xf0\xe0\x01\x00\xf0\xe0\x03\x00'\ 221 | b'\xf0\xe0\x0f\x00\xf0\xe0\x3f\x00\xf0\xf1\x7f\x00\xe0\xff\xfc\x00'\ 222 | b'\xe0\xff\xf0\x00\xc0\x7f\xe0\x00\x00\x1f\x80\x00\x00\x00\x00\x00'\ 223 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x04\x00\x80\x0f'\ 224 | b'\x1e\x00\xc0\x1f\x3e\x00\xe0\x3f\x7e\x00\xe0\x3f\x7c\x00\xf0\x39'\ 225 | b'\xf8\x00\xf0\x70\xf0\x00\xf0\x70\xf0\x00\xf0\x70\xf0\x00\xf0\x70'\ 226 | b'\xf0\x00\xf0\x60\xf0\x00\xf0\xe1\xf8\x00\xe0\xe3\x78\x00\xe0\xc7'\ 227 | b'\x7f\x00\xc0\xc3\x7f\x00\x80\x83\x3f\x00\x00\x02\x0f\x00\x00\x00'\ 228 | b'\x00\x00\x0f\x00\xf0\x00\x00\x00\xf0\x00\x00\x00\xf0\x00\x00\x00'\ 229 | b'\xf0\x00\x00\x00\xf0\x00\x00\x00\xf0\xff\xff\x00\xf0\xff\xff\x00'\ 230 | b'\xf0\xff\xff\x00\xf0\xff\xff\x00\xf0\x00\x00\x00\xf0\x00\x00\x00'\ 231 | b'\xf0\x00\x00\x00\xf0\x00\x00\x00\xf0\x00\x00\x00\x00\x00\x00\x00'\ 232 | b'\x12\x00\xf0\xff\x07\x00\xf0\xff\x1f\x00\xf0\xff\x3f\x00\xf0\xff'\ 233 | b'\x7f\x00\x00\x00\x7c\x00\x00\x00\xf8\x00\x00\x00\xf0\x00\x00\x00'\ 234 | b'\xf0\x00\x00\x00\xf0\x00\x00\x00\xf0\x00\x00\x00\xf8\x00\x00\x00'\ 235 | b'\x7c\x00\xf0\xff\x7f\x00\xf0\xff\x3f\x00\xf0\xff\x1f\x00\xf0\xff'\ 236 | b'\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x30\x00\x00\x00'\ 237 | b'\xf0\x01\x00\x00\xf0\x0f\x00\x00\xf0\x7f\x00\x00\xc0\xff\x03\x00'\ 238 | b'\x00\xfe\x1f\x00\x00\xe0\xff\x00\x00\x00\xff\x00\x00\x00\xf8\x00'\ 239 | b'\x00\x00\xff\x00\x00\xe0\xff\x00\x00\xfe\x1f\x00\xc0\xff\x03\x00'\ 240 | b'\xf0\x7f\x00\x00\xf0\x0f\x00\x00\xf0\x01\x00\x00\x30\x00\x00\x00'\ 241 | b'\x19\x00\x70\x00\x00\x00\xf0\x03\x00\x00\xf0\x3f\x00\x00\xf0\xff'\ 242 | b'\x01\x00\x80\xff\x1f\x00\x00\xf8\xff\x00\x00\x80\xff\x00\x00\x00'\ 243 | b'\xfc\x00\x00\xe0\xff\x00\x00\xff\xff\x00\xf0\xff\x07\x00\xf0\x3f'\ 244 | b'\x00\x00\xf0\x03\x00\x00\xf0\x1f\x00\x00\xf0\xff\x03\x00\x80\xff'\ 245 | b'\x7f\x00\x00\xf8\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\xf0'\ 246 | b'\xff\x00\x00\xff\x3f\x00\xf0\xff\x07\x00\xf0\x7f\x00\x00\xf0\x07'\ 247 | b'\x00\x00\xf0\x00\x00\x00\x11\x00\x10\x00\x80\x00\x30\x00\xe0\x00'\ 248 | b'\xf0\x00\xf0\x00\xf0\x01\xfc\x00\xf0\x07\xfe\x00\xc0\x9f\x3f\x00'\ 249 | b'\x80\xff\x0f\x00\x00\xfe\x07\x00\x00\xfc\x01\x00\x00\xfe\x07\x00'\ 250 | b'\x80\xff\x0f\x00\xc0\x9f\x3f\x00\xf0\x07\x7f\x00\xf0\x03\xfc\x00'\ 251 | b'\xf0\x00\xf8\x00\x70\x00\xe0\x00\x10\x00\x80\x00\x12\x00\x10\x00'\ 252 | b'\x00\x00\x30\x00\x00\x00\xf0\x00\x00\x00\xf0\x03\x00\x00\xf0\x0f'\ 253 | b'\x00\x00\xc0\x3f\x00\x00\x00\x7f\x00\x00\x00\xfc\xff\x00\x00\xf0'\ 254 | b'\xff\x00\x00\xf0\xff\x00\x00\xfc\xff\x00\x00\x7f\x00\x00\xc0\x3f'\ 255 | b'\x00\x00\xf0\x0f\x00\x00\xf0\x03\x00\x00\xf0\x00\x00\x00\x30\x00'\ 256 | b'\x00\x00\x10\x00\x00\x00\x11\x00\x00\x00\xe0\x00\xf0\x00\xf8\x00'\ 257 | b'\xf0\x00\xfc\x00\xf0\x00\xfe\x00\xf0\x00\xff\x00\xf0\x80\xff\x00'\ 258 | b'\xf0\xc0\xf3\x00\xf0\xf0\xf1\x00\xf0\xf8\xf0\x00\xf0\x7c\xf0\x00'\ 259 | b'\xf0\x3e\xf0\x00\xf0\x0f\xf0\x00\xf0\x07\xf0\x00\xf0\x03\xf0\x00'\ 260 | b'\xf0\x01\xf0\x00\xf0\x00\xf0\x00\x00\x00\x00\x00\x09\x00\xff\xff'\ 261 | b'\xff\x0f\xff\xff\xff\x0f\xff\xff\xff\x0f\xff\xff\xff\x0f\x0f\x00'\ 262 | b'\x00\x0f\x0f\x00\x00\x0f\x0f\x00\x00\x0f\x00\x00\x00\x00\x00\x00'\ 263 | b'\x00\x00\x0a\x00\x04\x00\x00\x00\x3c\x00\x00\x00\xfc\x03\x00\x00'\ 264 | b'\xfc\x1f\x00\x00\xf8\xff\x01\x00\xc0\xff\x1f\x00\x00\xfc\xff\x00'\ 265 | b'\x00\xe0\xff\x00\x00\x00\xfe\x00\x00\x00\xe0\x00\x09\x00\x0f\x00'\ 266 | b'\x00\x0f\x0f\x00\x00\x0f\x0f\x00\x00\x0f\xff\xff\xff\x0f\xff\xff'\ 267 | b'\xff\x0f\xff\xff\xff\x0f\xff\xff\xff\x0f\x00\x00\x00\x00\x00\x00'\ 268 | b'\x00\x00\x0f\x00\x00\x80\x01\x00\x00\xf0\x01\x00\x00\xfc\x01\x00'\ 269 | b'\x80\xff\x01\x00\xe0\x3f\x00\x00\xf0\x07\x00\x00\xf0\x01\x00\x00'\ 270 | b'\xf0\x03\x00\x00\xf0\x1f\x00\x00\xc0\xff\x00\x00\x00\xfe\x01\x00'\ 271 | b'\x00\xf8\x01\x00\x00\xc0\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00'\ 272 | b'\x17\x00\x00\x00\x00\x0f\x00\x00\x00\x0f\x00\x00\x00\x0f\x00\x00'\ 273 | b'\x00\x0f\x00\x00\x00\x0f\x00\x00\x00\x0f\x00\x00\x00\x0f\x00\x00'\ 274 | b'\x00\x0f\x00\x00\x00\x0f\x00\x00\x00\x0f\x00\x00\x00\x0f\x00\x00'\ 275 | b'\x00\x0f\x00\x00\x00\x0f\x00\x00\x00\x0f\x00\x00\x00\x0f\x00\x00'\ 276 | b'\x00\x0f\x00\x00\x00\x0f\x00\x00\x00\x0f\x00\x00\x00\x0f\x00\x00'\ 277 | b'\x00\x0f\x00\x00\x00\x0f\x00\x00\x00\x0f\x00\x00\x00\x0f\x0c\x00'\ 278 | b'\x08\x00\x00\x00\x18\x00\x00\x00\x38\x00\x00\x00\x78\x00\x00\x00'\ 279 | b'\x70\x00\x00\x00\x60\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00'\ 280 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 281 | b'\x11\x00\x00\x40\x3c\x00\x00\x30\x7e\x00\x00\x38\x7f\x00\x00\x7c'\ 282 | b'\xff\x00\x00\x3e\xf3\x00\x00\x1e\xf3\x00\x00\x1e\xf3\x00\x00\x1e'\ 283 | b'\xf1\x00\x00\x9e\x71\x00\x00\xbe\x79\x00\x00\xfc\xff\x00\x00\xfc'\ 284 | b'\xff\x00\x00\xf8\xff\x00\x00\xf0\xff\x00\x00\x00\x00\x00\x00\x00'\ 285 | b'\x00\x00\x00\x00\x00\x00\x12\x00\xfc\xff\xff\x00\xfc\xff\xff\x00'\ 286 | b'\xfc\xff\xff\x00\xfc\xff\xff\x00\x00\x7c\x3c\x00\x00\x3c\x78\x00'\ 287 | b'\x00\x1e\xf0\x00\x00\x1e\xf0\x00\x00\x1e\xf0\x00\x00\x1e\xf0\x00'\ 288 | b'\x00\x3e\xf8\x00\x00\x7c\x7c\x00\x00\xfc\x7f\x00\x00\xf8\x3f\x00'\ 289 | b'\x00\xf0\x1f\x00\x00\xc0\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 290 | b'\x10\x00\x00\xc0\x07\x00\x00\xf0\x1f\x00\x00\xf8\x3f\x00\x00\xfc'\ 291 | b'\x7f\x00\x00\x7c\x7c\x00\x00\x3e\xf8\x00\x00\x1e\xf0\x00\x00\x1e'\ 292 | b'\xf0\x00\x00\x1e\xf0\x00\x00\x1e\xf0\x00\x00\x3c\xf8\x00\x00\x7c'\ 293 | b'\x7c\x00\x00\x78\x38\x00\x00\x30\x18\x00\x00\x20\x08\x00\x00\x00'\ 294 | b'\x00\x00\x12\x00\x00\xc0\x07\x00\x00\xf0\x1f\x00\x00\xf8\x3f\x00'\ 295 | b'\x00\xfc\x7f\x00\x00\x7e\xfc\x00\x00\x3e\xf8\x00\x00\x1e\xf0\x00'\ 296 | b'\x00\x1e\xf0\x00\x00\x1e\xf0\x00\x00\x3c\x78\x00\x00\x7c\x7c\x00'\ 297 | b'\xfc\xff\xff\x00\xfc\xff\xff\x00\xfc\xff\xff\x00\xfc\xff\xff\x00'\ 298 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\xe0'\ 299 | b'\x0f\x00\x00\xf0\x1f\x00\x00\xf8\x3f\x00\x00\xfc\x7f\x00\x00\xdc'\ 300 | b'\x7b\x00\x00\xde\xf3\x00\x00\xde\xf3\x00\x00\xde\xf3\x00\x00\xde'\ 301 | b'\xf3\x00\x00\xde\xfb\x00\x00\xfc\x7b\x00\x00\xfc\x7b\x00\x00\xf8'\ 302 | b'\x33\x00\x00\xf0\x03\x00\x00\x80\x03\x00\x00\x00\x00\x00\x09\x00'\ 303 | b'\x00\x1c\x00\x00\x00\x1c\x00\x00\xc0\xff\xff\x00\xf0\xff\xff\x00'\ 304 | b'\xf0\xff\xff\x00\xf8\xff\xff\x00\x78\x1c\x00\x00\x3c\x1c\x00\x00'\ 305 | b'\x3c\x1c\x00\x00\x11\x00\x00\xc0\x07\x08\x00\xf0\x1f\x1c\x00\xf8'\ 306 | b'\x3f\x3c\x00\xfc\x7f\x3c\x00\x7e\xfc\x7c\x00\x3e\xf8\x78\x00\x1e'\ 307 | b'\xf0\x78\x00\x1e\xf0\x78\x00\x1e\xf0\x78\x00\x3c\x78\x7c\x00\x78'\ 308 | b'\x7c\x3e\x00\xfe\xff\x3f\x00\xfe\xff\x1f\x00\xfe\xff\x0f\x00\xfe'\ 309 | b'\xff\x03\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\xfc\xff\xff\x00'\ 310 | b'\xfc\xff\xff\x00\xfc\xff\xff\x00\xfc\xff\xff\x00\x00\x3c\x00\x00'\ 311 | b'\x00\x1e\x00\x00\x00\x1e\x00\x00\x00\x1e\x00\x00\x00\x1e\x00\x00'\ 312 | b'\x00\x3e\x00\x00\x00\xfc\xff\x00\x00\xfc\xff\x00\x00\xf8\xff\x00'\ 313 | b'\x00\xe0\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00\x60\xfe'\ 314 | b'\xff\x00\xf0\xfe\xff\x00\xf0\xfe\xff\x00\x60\xfe\xff\x00\x00\x00'\ 315 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x78'\ 316 | b'\x00\x00\x00\x78\x60\xfe\xff\x7f\xf0\xfe\xff\x3f\xf0\xfe\xff\x3f'\ 317 | b'\x60\xfe\xff\x0f\x10\x00\xfc\xff\xff\x00\xfc\xff\xff\x00\xfc\xff'\ 318 | b'\xff\x00\xfc\xff\xff\x00\x00\xc0\x07\x00\x00\xe0\x03\x00\x00\xf0'\ 319 | b'\x07\x00\x00\xfc\x0f\x00\x00\xfe\x3f\x00\x00\x3e\xff\x00\x00\x1e'\ 320 | b'\xfc\x00\x00\x0e\xf8\x00\x00\x06\xe0\x00\x00\x02\xc0\x00\x00\x00'\ 321 | b'\x80\x00\x00\x00\x00\x00\x07\x00\xfc\xff\xff\x00\xfc\xff\xff\x00'\ 322 | b'\xfc\xff\xff\x00\xfc\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 323 | b'\x00\x00\x00\x00\x1a\x00\x00\xfe\xff\x00\x00\xfe\xff\x00\x00\xfe'\ 324 | b'\xff\x00\x00\xfe\xff\x00\x00\x3c\x00\x00\x00\x1e\x00\x00\x00\x1e'\ 325 | b'\x00\x00\x00\x1e\x00\x00\x00\x1e\x00\x00\x00\x3e\x00\x00\x00\xfc'\ 326 | b'\xff\x00\x00\xfc\xff\x00\x00\xf8\xff\x00\x00\xfc\xff\x00\x00\x3e'\ 327 | b'\x00\x00\x00\x1e\x00\x00\x00\x1e\x00\x00\x00\x1e\x00\x00\x00\x3e'\ 328 | b'\x00\x00\x00\xfc\xff\x00\x00\xfc\xff\x00\x00\xf8\xff\x00\x00\xe0'\ 329 | b'\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00'\ 330 | b'\x00\xfe\xff\x00\x00\xfe\xff\x00\x00\xfe\xff\x00\x00\xfe\xff\x00'\ 331 | b'\x00\x3c\x00\x00\x00\x1c\x00\x00\x00\x1e\x00\x00\x00\x1e\x00\x00'\ 332 | b'\x00\x1e\x00\x00\x00\x3e\x00\x00\x00\xfc\xff\x00\x00\xfc\xff\x00'\ 333 | b'\x00\xf8\xff\x00\x00\xe0\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 334 | b'\x11\x00\x00\xc0\x07\x00\x00\xf0\x1f\x00\x00\xf8\x3f\x00\x00\xfc'\ 335 | b'\x7f\x00\x00\x7c\x7c\x00\x00\x3e\xf8\x00\x00\x1e\xf0\x00\x00\x1e'\ 336 | b'\xf0\x00\x00\x1e\xf0\x00\x00\x3e\xf8\x00\x00\x7c\x7c\x00\x00\xfc'\ 337 | b'\x7f\x00\x00\xf8\x3f\x00\x00\xf0\x1f\x00\x00\xc0\x07\x00\x00\x00'\ 338 | b'\x00\x00\x00\x00\x00\x00\x12\x00\x00\xfe\xff\x7f\x00\xfe\xff\x7f'\ 339 | b'\x00\xfe\xff\x7f\x00\xfe\xff\x7f\x00\x7c\x7c\x00\x00\x3c\x78\x00'\ 340 | b'\x00\x1e\xf0\x00\x00\x1e\xf0\x00\x00\x1e\xf0\x00\x00\x1e\xf0\x00'\ 341 | b'\x00\x3e\xf8\x00\x00\x7c\x7c\x00\x00\xfc\x7f\x00\x00\xf8\x3f\x00'\ 342 | b'\x00\xf0\x1f\x00\x00\xc0\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 343 | b'\x11\x00\x00\xc0\x07\x00\x00\xf0\x1f\x00\x00\xf8\x3f\x00\x00\xfc'\ 344 | b'\x7f\x00\x00\x7e\xfc\x00\x00\x3e\xf8\x00\x00\x1e\xf0\x00\x00\x1e'\ 345 | b'\xf0\x00\x00\x1e\xf0\x00\x00\x3c\x78\x00\x00\x7c\x7c\x00\x00\xfe'\ 346 | b'\xff\x7f\x00\xfe\xff\x7f\x00\xfe\xff\x7f\x00\xfe\xff\x7f\x00\x00'\ 347 | b'\x00\x00\x00\x00\x00\x00\x0a\x00\x00\xfe\xff\x00\x00\xfe\xff\x00'\ 348 | b'\x00\xfe\xff\x00\x00\xfe\xff\x00\x00\x3c\x00\x00\x00\x1e\x00\x00'\ 349 | b'\x00\x1e\x00\x00\x00\x0e\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00'\ 350 | b'\x0f\x00\x00\x00\x08\x00\x00\x70\x18\x00\x00\xfc\x38\x00\x00\xfc'\ 351 | b'\x7d\x00\x00\xfe\xf9\x00\x00\x9e\xf1\x00\x00\x9e\xf1\x00\x00\x9e'\ 352 | b'\xf1\x00\x00\x9e\xf3\x00\x00\x3e\xf3\x00\x00\x3c\x7f\x00\x00\x3c'\ 353 | b'\x7f\x00\x00\x38\x3e\x00\x00\x20\x1c\x00\x00\x00\x00\x00\x09\x00'\ 354 | b'\x00\x0e\x00\x00\x00\x0e\x00\x00\xfc\xff\x1f\x00\xfc\xff\x3f\x00'\ 355 | b'\xfc\xff\x7f\x00\xfc\xff\xff\x00\x00\x0e\xf0\x00\x00\x0e\xf0\x00'\ 356 | b'\x00\x0e\xf0\x00\x10\x00\x00\xfe\x0f\x00\x00\xfe\x3f\x00\x00\xfe'\ 357 | b'\x7f\x00\x00\xfe\x7f\x00\x00\x00\xf8\x00\x00\x00\xf0\x00\x00\x00'\ 358 | b'\xf0\x00\x00\x00\xf0\x00\x00\x00\x70\x00\x00\x00\x78\x00\x00\xfe'\ 359 | b'\xff\x00\x00\xfe\xff\x00\x00\xfe\xff\x00\x00\xfe\xff\x00\x00\x00'\ 360 | b'\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x06\x00\x00\x00\x3e\x00\x00'\ 361 | b'\x00\xfe\x00\x00\x00\xfe\x07\x00\x00\xf8\x3f\x00\x00\xc0\xff\x00'\ 362 | b'\x00\x00\xfe\x00\x00\x00\xf0\x00\x00\x00\xfe\x00\x00\xc0\xff\x00'\ 363 | b'\x00\xf8\x1f\x00\x00\xfe\x07\x00\x00\xfe\x00\x00\x00\x1e\x00\x00'\ 364 | b'\x00\x06\x00\x00\x16\x00\x00\x06\x00\x00\x00\x7e\x00\x00\x00\xfe'\ 365 | b'\x03\x00\x00\xfe\x1f\x00\x00\xf0\xff\x00\x00\x00\xff\x00\x00\x00'\ 366 | b'\xf8\x00\x00\x80\xff\x00\x00\xf8\xff\x00\x00\xfe\x0f\x00\x00\xfe'\ 367 | b'\x00\x00\x00\xfe\x00\x00\x00\xfe\x07\x00\x00\xf8\x7f\x00\x00\x80'\ 368 | b'\xff\x00\x00\x00\xf8\x00\x00\x00\xff\x00\x00\xf0\xff\x00\x00\xfe'\ 369 | b'\x3f\x00\x00\xfe\x03\x00\x00\x7e\x00\x00\x00\x0e\x00\x00\x0f\x00'\ 370 | b'\x00\x02\x80\x00\x00\x06\xe0\x00\x00\x1e\xf0\x00\x00\x3e\xfc\x00'\ 371 | b'\x00\xfe\x7e\x00\x00\xf8\x3f\x00\x00\xe0\x0f\x00\x00\xe0\x07\x00'\ 372 | b'\x00\xf8\x1f\x00\x00\xfc\x7f\x00\x00\x7e\xfc\x00\x00\x1e\xf8\x00'\ 373 | b'\x00\x0e\xe0\x00\x00\x02\xc0\x00\x00\x00\x80\x00\x0f\x00\x00\x06'\ 374 | b'\x00\x00\x00\x3e\x00\x00\x00\xfe\x00\x78\x00\xfe\x07\x78\x00\xf8'\ 375 | b'\x1f\x7c\x00\xc0\xff\x7f\x00\x00\xfe\x3f\x00\x00\xf0\x1f\x00\x00'\ 376 | b'\xfe\x07\x00\xc0\xff\x00\x00\xf8\x1f\x00\x00\xfe\x07\x00\x00\xfe'\ 377 | b'\x00\x00\x00\x1e\x00\x00\x00\x06\x00\x00\x0e\x00\x00\x00\xe0\x00'\ 378 | b'\x00\x1e\xf0\x00\x00\x1e\xf8\x00\x00\x1e\xfc\x00\x00\x1e\xfe\x00'\ 379 | b'\x00\x1e\xff\x00\x00\x9e\xf7\x00\x00\xde\xf3\x00\x00\xfe\xf1\x00'\ 380 | b'\x00\xfe\xf0\x00\x00\x7e\xf0\x00\x00\x3e\xf0\x00\x00\x1e\xf0\x00'\ 381 | b'\x00\x00\x00\x00\x0a\x00\x00\x60\x00\x00\x00\xf0\x00\x00\x00\xf0'\ 382 | b'\x00\x00\xf8\xff\xff\x01\xfc\xff\xff\x03\xfe\x9f\xff\x07\xfe\x0f'\ 383 | b'\xff\x07\x0e\x00\x00\x07\x0e\x00\x00\x07\x00\x00\x00\x00\x08\x00'\ 384 | b'\xfc\xff\xff\x00\xfc\xff\xff\x00\xfc\xff\xff\x00\xfc\xff\xff\x00'\ 385 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 386 | b'\x09\x00\x0e\x00\x00\x07\x0e\x00\x00\x07\xfe\x0f\xff\x07\xfe\x9f'\ 387 | b'\xff\x07\xfc\xff\xff\x03\xf8\xff\xff\x01\x00\xf0\x00\x00\x00\xf0'\ 388 | b'\x00\x00\x00\x60\x00\x00\x0c\x00\x30\x00\x00\x00\x38\x00\x00\x00'\ 389 | b'\x18\x00\x00\x00\x18\x00\x00\x00\x18\x00\x00\x00\x30\x00\x00\x00'\ 390 | b'\x30\x00\x00\x00\x30\x00\x00\x00\x38\x00\x00\x00\x18\x00\x00\x00'\ 391 | b'\x00\x00\x00\x00\x00\x00\x00\x00' 392 | 393 | _index =\ 394 | b'\x00\x00\x3e\x00\x64\x00\x86\x00\xb0\x00\xee\x00\x28\x01\x86\x01'\ 395 | b'\xd4\x01\xea\x01\x10\x02\x36\x02\x64\x02\xa2\x02\xbc\x02\xe2\x02'\ 396 | b'\xfc\x02\x26\x03\x6c\x03\x92\x03\xd4\x03\x16\x04\x58\x04\x96\x04'\ 397 | b'\xd8\x04\x12\x05\x54\x05\x96\x05\xb0\x05\xca\x05\xfc\x05\x36\x06'\ 398 | b'\x68\x06\xa6\x06\x00\x07\x4e\x07\x98\x07\xea\x07\x38\x08\x7e\x08'\ 399 | b'\xc0\x08\x16\x09\x68\x09\x86\x09\xc4\x09\x0e\x0a\x50\x0a\xb2\x0a'\ 400 | b'\x04\x0b\x5e\x0b\xa8\x0b\xfe\x0b\x48\x0c\x92\x0c\xd0\x0c\x1a\x0d'\ 401 | b'\x60\x0d\xc6\x0d\x0c\x0e\x56\x0e\x9c\x0e\xc2\x0e\xec\x0e\x12\x0f'\ 402 | b'\x50\x0f\xae\x0f\xe0\x0f\x26\x10\x70\x10\xb2\x10\xfc\x10\x3e\x11'\ 403 | b'\x64\x11\xaa\x11\xec\x11\x0a\x12\x24\x12\x66\x12\x84\x12\xee\x12'\ 404 | b'\x30\x13\x76\x13\xc0\x13\x06\x14\x30\x14\x6e\x14\x94\x14\xd6\x14'\ 405 | b'\x14\x15\x6e\x15\xac\x15\xea\x15\x24\x16\x4e\x16\x70\x16\x96\x16'\ 406 | b'\xc8\x16' 407 | 408 | _mvfont = memoryview(_font) 409 | 410 | def _chr_addr(ordch): 411 | offset = 2 * (ordch - 32) 412 | return int.from_bytes(_index[offset:offset + 2], 'little') 413 | 414 | def get_width(s): 415 | width = 0 416 | for ch in s: 417 | ordch = ord(ch) 418 | ordch = ordch + 1 if ordch >= 32 and ordch <= 126 else 32 419 | offset = _chr_addr(ordch) 420 | width += int.from_bytes(_font[offset:offset + 2], 'little') 421 | return width 422 | 423 | def get_ch(ch): 424 | ordch = ord(ch) 425 | ordch = ordch + 1 if ordch >= 32 and ordch <= 126 else 32 426 | offset = _chr_addr(ordch) 427 | width = int.from_bytes(_font[offset:offset + 2], 'little') 428 | next_offs = _chr_addr(ordch +1) 429 | return _mvfont[offset + 2:next_offs], width 430 | 431 | -------------------------------------------------------------------------------- /hcsr04.py: -------------------------------------------------------------------------------- 1 | import time 2 | from uos import uname 3 | from machine import Pin 4 | 5 | try: 6 | from machine import time_pulse_us 7 | except: 8 | from pycom import pulses_get 9 | 10 | #__author__ = 'Roberto Sánchez' 11 | #__license__= "Apache License 2.0. https://www.apache.org/licenses/LICENSE-2.0" 12 | #__author-update__ = "Mauro Riva" 13 | 14 | class HCSR04: 15 | """ 16 | Driver to use the untrasonic sensor HC-SR04. 17 | The sensor range is between 2cm and 4m. 18 | 19 | The timeouts received listening to echo pin are converted to OSError('Out of range') 20 | 21 | """ 22 | # echo_timeout_us is based in chip range limit (400cm) 23 | def __init__(self, trigger_pin, echo_pin, echo_timeout_us=500*2*30): 24 | """ 25 | trigger_pin: Output pin to send pulses 26 | echo_pin: Readonly pin to measure the distance. The pin should be protected with 1k resistor 27 | echo_timeout_us: Timeout in microseconds to listen to echo pin. 28 | By default is based in sensor limit range (4m) 29 | """ 30 | self.echo_timeout_us = echo_timeout_us 31 | # Init trigger pin (out) 32 | self.trigger = Pin(trigger_pin, mode=Pin.OUT) 33 | self.trigger.value(0) 34 | # Init echo pin (in) 35 | if (uname().sysname == 'WiPy'): 36 | self.echo = Pin(echo_pin, mode=Pin.OPEN_DRAIN) 37 | else: 38 | self.echo = Pin(echo_pin, mode=Pin.IN) 39 | 40 | def _send_pulse_and_wait(self): 41 | """ 42 | Send the pulse to trigger and listen on echo pin. 43 | We use the method `machine.time_pulse_us()` to get the microseconds until the echo is received. 44 | """ 45 | self.trigger.value(0) # Stabilize the sensor 46 | time.sleep_us(5) 47 | self.trigger.value(1) 48 | # Send a 10us pulse. 49 | time.sleep_us(10) 50 | self.trigger.value(0) 51 | try: 52 | if (uname().sysname == 'WiPy'): 53 | pulse_list = pulses_get(self.echo, self.echo_timeout_us) 54 | if(len(pulse_list) == 0): 55 | pulse_time = -1 56 | else: 57 | pulse_time = pulse_list[0][1] 58 | else: 59 | pulse_time = time_pulse_us(self.echo, 1, self.echo_timeout_us) 60 | 61 | return pulse_time 62 | except OSError as ex: 63 | if ex.args[0] == 110: # 110 = ETIMEDOUT 64 | raise OSError('Out of range') 65 | raise ex 66 | 67 | def distance_mm(self): 68 | """ 69 | Get the distance in milimeters without floating point operations. 70 | """ 71 | pulse_time = self._send_pulse_and_wait() 72 | 73 | # To calculate the distance we get the pulse_time and divide it by 2 74 | # (the pulse walk the distance twice) and by 29.1 becasue 75 | # the sound speed on air (343.2 m/s), that It's equivalent to 76 | # 0.34320 mm/us that is 1mm each 2.91us 77 | # pulse_time // 2 // 2.91 -> pulse_time // 5.82 -> pulse_time * 100 // 582 78 | mm = pulse_time * 100 // 582 79 | return mm 80 | 81 | def distance_cm(self): 82 | """ 83 | Get the distance in centimeters with floating point operations. 84 | It returns a float 85 | """ 86 | pulse_time = self._send_pulse_and_wait() 87 | 88 | # To calculate the distance we get the pulse_time and divide it by 2 89 | # (the pulse walk the distance twice) and by 29.1 becasue 90 | # the sound speed on air (343.2 m/s), that It's equivalent to 91 | # 0.034320 cm/us that is 1cm each 29.1us 92 | cms = (pulse_time / 2) / 29.1 93 | return cms 94 | -------------------------------------------------------------------------------- /ili934xnew.py: -------------------------------------------------------------------------------- 1 | # This is an adapted version of the ILI934X driver as below. 2 | # It works with multiple fonts and also works with the esp32 H/W SPI implementation 3 | # Also includes a word wrap print function 4 | # Proportional fonts are generated by Peter Hinch's Font-to-py 5 | # MIT License; Copyright (c) 2017 Jeffrey N. Magee 6 | 7 | # This file is part of MicroPython ILI934X driver 8 | # Copyright (c) 2016 - 2017 Radomir Dopieralski, Mika Tuupola 9 | # 10 | # Licensed under the MIT license: 11 | # http://www.opensource.org/licenses/mit-license.php 12 | # 13 | # Project home: 14 | # https://github.com/tuupola/micropython-ili934x 15 | 16 | import time 17 | import ustruct 18 | import framebuf 19 | from micropython import const 20 | 21 | _RDDSDR = const(0x0f) # Read Display Self-Diagnostic Result 22 | _SLPOUT = const(0x11) # Sleep Out 23 | _GAMSET = const(0x26) # Gamma Set 24 | _DISPOFF = const(0x28) # Display Off 25 | _DISPON = const(0x29) # Display On 26 | _CASET = const(0x2a) # Column Address Set 27 | _PASET = const(0x2b) # Page Address Set 28 | _RAMWR = const(0x2c) # Memory Write 29 | _RAMRD = const(0x2e) # Memory Read 30 | _MADCTL = const(0x36) # Memory Access Control 31 | _VSCRSADD = const(0x37) # Vertical Scrolling Start Address 32 | _PIXSET = const(0x3a) # Pixel Format Set 33 | _PWCTRLA = const(0xcb) # Power Control A 34 | _PWCRTLB = const(0xcf) # Power Control B 35 | _DTCTRLA = const(0xe8) # Driver Timing Control A 36 | _DTCTRLB = const(0xea) # Driver Timing Control B 37 | _PWRONCTRL = const(0xed) # Power on Sequence Control 38 | _PRCTRL = const(0xf7) # Pump Ratio Control 39 | _PWCTRL1 = const(0xc0) # Power Control 1 40 | _PWCTRL2 = const(0xc1) # Power Control 2 41 | _VMCTRL1 = const(0xc5) # VCOM Control 1 42 | _VMCTRL2 = const(0xc7) # VCOM Control 2 43 | _FRMCTR1 = const(0xb1) # Frame Rate Control 1 44 | _DISCTRL = const(0xb6) # Display Function Control 45 | _ENA3G = const(0xf2) # Enable 3G 46 | _PGAMCTRL = const(0xe0) # Positive Gamma Control 47 | _NGAMCTRL = const(0xe1) # Negative Gamma Control 48 | 49 | _CHUNK = const(1024) #maximum number of pixels per spi write 50 | 51 | def color565(r, g, b): 52 | return (r & 0xf8) << 8 | (g & 0xfc) << 3 | b >> 3 53 | 54 | class ILI9341: 55 | 56 | def __init__(self, spi, cs, dc, rst, w, h, r, font): 57 | self.spi = spi 58 | self.cs = cs 59 | self.dc = dc 60 | self.rst = rst 61 | self._init_width = w 62 | self._init_height = h 63 | self.width = w 64 | self.height = h 65 | self.rotation = r 66 | self.cs.init(self.cs.OUT, value=1) 67 | self.dc.init(self.dc.OUT, value=0) 68 | self.rst.init(self.rst.OUT, value=0) 69 | self.reset() 70 | self.init() 71 | self._scroll = 0 72 | self._buf = bytearray(_CHUNK * 2) 73 | self._colormap = bytearray(b'\x00\x00\xFF\xFF') #default white foregraound, black background 74 | self._x = 0 75 | self._y = 0 76 | self._font = font 77 | self.scrolling = False 78 | 79 | def set_color(self, fg, bg): 80 | self._colormap[0] = bg>>8 81 | self._colormap[1] = bg & 255 82 | self._colormap[2] = fg>>8 83 | self._colormap[3] = fg & 255 84 | 85 | def set_pos(self, x, y): 86 | self._x = x 87 | self._y = y 88 | 89 | def reset_scroll(self): 90 | self.scrolling = False 91 | self._scroll = 0 92 | self.scroll(0) 93 | 94 | def set_font(self, font): 95 | self._font = font 96 | 97 | def init(self): 98 | for command, data in ( 99 | (_RDDSDR, b"\x03\x80\x02"), 100 | (_PWCRTLB, b"\x00\xc1\x30"), 101 | (_PWRONCTRL, b"\x64\x03\x12\x81"), 102 | (_DTCTRLA, b"\x85\x00\x78"), 103 | (_PWCTRLA, b"\x39\x2c\x00\x34\x02"), 104 | (_PRCTRL, b"\x20"), 105 | (_DTCTRLB, b"\x00\x00"), 106 | (_PWCTRL1, b"\x23"), 107 | (_PWCTRL2, b"\x10"), 108 | (_VMCTRL1, b"\x3e\x28"), 109 | (_VMCTRL2, b"\x86")): 110 | self._write(command, data) 111 | 112 | if self.rotation == 0: # 0 deg 113 | self._write(_MADCTL, b"\x48") 114 | self.width = self._init_height 115 | self.height = self._init_width 116 | elif self.rotation == 1: # 90 deg 117 | self._write(_MADCTL, b"\x28") 118 | self.width = self._init_width 119 | self.height = self._init_height 120 | elif self.rotation == 2: # 180 deg 121 | self._write(_MADCTL, b"\x88") 122 | self.width = self._init_height 123 | self.height = self._init_width 124 | elif self.rotation == 3: # 270 deg 125 | self._write(_MADCTL, b"\xE8") 126 | self.width = self._init_width 127 | self.height = self._init_height 128 | elif self.rotation == 4: # Mirrored + 0 deg 129 | self._write(_MADCTL, b"\xC8") 130 | self.width = self._init_height 131 | self.height = self._init_width 132 | elif self.rotation == 5: # Mirrored + 90 deg 133 | self._write(_MADCTL, b"\x68") 134 | self.width = self._init_width 135 | self.height = self._init_height 136 | elif self.rotation == 6: # Mirrored + 180 deg 137 | self._write(_MADCTL, b"\x08") 138 | self.width = self._init_height 139 | self.height = self._init_width 140 | elif self.rotation == 7: # Mirrored + 270 deg 141 | self._write(_MADCTL, b"\xA8") 142 | self.width = self._init_width 143 | self.height = self._init_height 144 | else: 145 | self._write(_MADCTL, b"\x08") 146 | 147 | for command, data in ( 148 | (_PIXSET, b"\x55"), 149 | (_FRMCTR1, b"\x00\x18"), 150 | (_DISCTRL, b"\x08\x82\x27"), 151 | (_ENA3G, b"\x00"), 152 | (_GAMSET, b"\x01"), 153 | (_PGAMCTRL, b"\x0f\x31\x2b\x0c\x0e\x08\x4e\xf1\x37\x07\x10\x03\x0e\x09\x00"), 154 | (_NGAMCTRL, b"\x00\x0e\x14\x03\x11\x07\x31\xc1\x48\x08\x0f\x0c\x31\x36\x0f")): 155 | self._write(command, data) 156 | self._write(_SLPOUT) 157 | time.sleep_ms(120) 158 | self._write(_DISPON) 159 | 160 | def reset(self): 161 | self.rst(0) 162 | time.sleep_ms(50) 163 | self.rst(1) 164 | time.sleep_ms(50) 165 | 166 | def _write(self, command, data=None): 167 | self.dc(0) 168 | self.cs(0) 169 | self.spi.write(bytearray([command])) 170 | self.cs(1) 171 | if data is not None: 172 | self._data(data) 173 | 174 | def _data(self, data): 175 | self.dc(1) 176 | self.cs(0) 177 | self.spi.write(data) 178 | self.cs(1) 179 | 180 | def _writeblock(self, x0, y0, x1, y1, data=None): 181 | self._write(_CASET, ustruct.pack(">HH", x0, x1)) 182 | self._write(_PASET, ustruct.pack(">HH", y0, y1)) 183 | self._write(_RAMWR, data) 184 | 185 | def _readblock(self, x0, y0, x1, y1): 186 | self._write(_CASET, ustruct.pack(">HH", x0, x1)) 187 | self._write(_PASET, ustruct.pack(">HH", y0, y1)) 188 | if data is None: 189 | return self._read(_RAMRD, (x1 - x0 + 1) * (y1 - y0 + 1) * 3) 190 | 191 | def _read(self, command, count): 192 | self.dc(0) 193 | self.cs(0) 194 | self.spi.write(bytearray([command])) 195 | data = self.spi.read(count) 196 | self.cs(1) 197 | return data 198 | 199 | def pixel(self, x, y, color=None): 200 | if color is None: 201 | r, b, g = self._readblock(x, y, x, y) 202 | return color565(r, g, b) 203 | if not 0 <= x < self.width or not 0 <= y < self.height: 204 | return 205 | self._writeblock(x, y, x, y, ustruct.pack(">H", color)) 206 | 207 | def fill_rectangle(self, x, y, w, h, color=None): 208 | x = min(self.width - 1, max(0, x)) 209 | y = min(self.height - 1, max(0, y)) 210 | w = min(self.width - x, max(1, w)) 211 | h = min(self.height - y, max(1, h)) 212 | if color: 213 | color = ustruct.pack(">H", color) 214 | else: 215 | color = self._colormap[0:2] #background 216 | for i in range(_CHUNK): 217 | self._buf[2*i]=color[0]; self._buf[2*i+1]=color[1] 218 | chunks, rest = divmod(w * h, _CHUNK) 219 | self._writeblock(x, y, x + w - 1, y + h - 1, None) 220 | if chunks: 221 | for count in range(chunks): 222 | self._data(self._buf) 223 | if rest != 0: 224 | mv = memoryview(self._buf) 225 | self._data(mv[:rest*2]) 226 | 227 | def erase(self): 228 | self.fill_rectangle(0, 0, self.width, self.height) 229 | 230 | def blit(self, bitbuff, x, y, w, h): 231 | x = min(self.width - 1, max(0, x)) 232 | y = min(self.height - 1, max(0, y)) 233 | w = min(self.width - x, max(1, w)) 234 | h = min(self.height - y, max(1, h)) 235 | chunks, rest = divmod(w * h, _CHUNK) 236 | self._writeblock(x, y, x + w - 1, y + h - 1, None) 237 | written = 0 238 | for iy in range(h): 239 | for ix in range(w): 240 | index = ix+iy*w - written 241 | if index >=_CHUNK: 242 | self._data(self._buf) 243 | written += _CHUNK 244 | index -= _CHUNK 245 | c = bitbuff.pixel(ix,iy) 246 | self._buf[index*2] = self._colormap[c*2] 247 | self._buf[index*2+1] = self._colormap[c*2+1] 248 | rest = w*h - written 249 | if rest != 0: 250 | mv = memoryview(self._buf) 251 | self._data(mv[:rest*2]) 252 | 253 | def chars(self, str, x, y): 254 | str_w = self._font.get_width(str) 255 | div, rem = divmod(self._font.height(),8) 256 | nbytes = div+1 if rem else div 257 | buf = bytearray(str_w * nbytes) 258 | pos = 0 259 | for ch in str: 260 | glyph, char_w = self._font.get_ch(ch) 261 | for row in range(nbytes): 262 | index = row*str_w + pos 263 | for i in range(char_w): 264 | buf[index+i] = glyph[nbytes*i+row] 265 | pos += char_w 266 | fb = framebuf.FrameBuffer(buf,str_w, self._font.height(), framebuf.MONO_VLSB) 267 | self.blit(fb,x,y,str_w,self._font.height()) 268 | return x+str_w 269 | 270 | def scroll(self, dy): 271 | self._scroll = (self._scroll + dy) % self.height 272 | self._write(_VSCRSADD, ustruct.pack(">H", self._scroll)) 273 | 274 | def next_line(self, cury, char_h): 275 | global scrolling 276 | if not self.scrolling: 277 | res = cury + char_h 278 | self.scrolling = (res >= self.height) 279 | if self.scrolling: 280 | self.scroll(char_h) 281 | res = (self.height - char_h + self._scroll)%self.height 282 | self.fill_rectangle(0, res, self.width, self._font.height()) 283 | return res 284 | 285 | def write(self, text): #does character wrap, compatible with stream output 286 | curx = self._x; cury = self._y 287 | char_h = self._font.height() 288 | width = 0 289 | written = 0 290 | for pos, ch in enumerate(text): 291 | if ch == '\n': 292 | if pos>0: 293 | self.chars(text[written:pos],curx,cury) 294 | curx = 0; written = pos+1; width = 0 295 | cury = self.next_line(cury,char_h) 296 | else: 297 | char_w = self._font.get_width(ch) 298 | if curx + width + char_w >= self.width: 299 | self.chars(text[written:pos], curx,cury) 300 | curx = 0 ; written = pos; width = char_h 301 | cury = self.next_line(cury,char_h) 302 | else: 303 | width += char_w 304 | if written= self.width: 318 | curx = self._x; cury = self.next_line(cury,char_h) 319 | while self._font.get_width(word) > self.width: 320 | self.chars(word[:self.width//char_w],curx,cury) 321 | word = word[self.width//char_w:] 322 | cury = self.next_line(cury,char_h) 323 | if len(word)>0: 324 | curx = self.chars(word+' ', curx,cury) 325 | curx = self._x; cury = self.next_line(cury,char_h) 326 | self._y = cury 327 | -------------------------------------------------------------------------------- /imu.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # imu.py MicroPython driver for the InvenSense inertial measurement units 3 | # This is the base class 4 | # Adapted from Sebastian Plamauer's MPU9150 driver: 5 | # https://github.com/micropython-IMU/micropython-mpu9150.git 6 | # Authors Peter Hinch, Sebastian Plamauer 7 | # V0.2 17th May 2017 Platform independent: utime and machine replace pyb 8 | 9 | ''' 10 | mpu9250 is a micropython module for the InvenSense MPU9250 sensor. 11 | It measures acceleration, turn rate and the magnetic field in three axis. 12 | mpu9150 driver modified for the MPU9250 by Peter Hinch 13 | 14 | The MIT License (MIT) 15 | Copyright (c) 2014 Sebastian Plamauer, oeplse@gmail.com, Peter Hinch 16 | Permission is hereby granted, free of charge, to any person obtaining a copy 17 | of this software and associated documentation files (the "Software"), to deal 18 | in the Software without restriction, including without limitation the rights 19 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 20 | copies of the Software, and to permit persons to whom the Software is 21 | furnished to do so, subject to the following conditions: 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 | THE SOFTWARE. 31 | ''' 32 | 33 | # User access is now by properties e.g. 34 | # myimu = MPU9250('X') 35 | # magx = myimu.mag.x 36 | # accelxyz = myimu.accel.xyz 37 | # Error handling: on code used for initialisation, abort with message 38 | # At runtime try to continue returning last good data value. We don't want aircraft 39 | # crashing. However if the I2C has crashed we're probably stuffed. 40 | 41 | from utime import sleep_ms 42 | from machine import I2C, Pin 43 | from .vector3d import Vector3d 44 | from os import uname 45 | 46 | 47 | class MPUException(OSError): 48 | ''' 49 | Exception for MPU devices 50 | ''' 51 | pass 52 | 53 | 54 | def bytes_toint(msb, lsb): 55 | ''' 56 | Convert two bytes to signed integer (big endian) 57 | for little endian reverse msb, lsb arguments 58 | Can be used in an interrupt handler 59 | ''' 60 | if not msb & 0x80: 61 | return msb << 8 | lsb # +ve 62 | return - (((msb ^ 255) << 8) | (lsb ^ 255) + 1) 63 | 64 | 65 | class MPU6050(object): 66 | ''' 67 | Module for InvenSense IMUs. Base class implements MPU6050 6DOF sensor, with 68 | features common to MPU9150 and MPU9250 9DOF sensors. 69 | ''' 70 | 71 | _I2Cerror = "I2C failure when communicating with IMU" 72 | _mpu_addr = (104, 105) # addresses of MPU9150/MPU6050. There can be two devices 73 | _chip_id = 104 # MPU6050 74 | 75 | def __init__(self, side_str, dev_pin=(15, 4), device_addr=None, transposition=(0, 1, 2), scaling=(1, 1, 1)): 76 | 77 | self._accel = Vector3d(transposition, scaling, self._accel_callback) 78 | self._gyro = Vector3d(transposition, scaling, self._gyro_callback) 79 | self.buf1 = bytearray(1) # Pre-allocated buffers for reads: allows reads to 80 | self.buf2 = bytearray(2) # be done in interrupt handlers 81 | self.buf3 = bytearray(3) 82 | self.buf6 = bytearray(6) 83 | 84 | sleep_ms(200) # Ensure PSU and device have settled 85 | 86 | #if(uname().sysname == 'WiPy'): 87 | # self._mpu_i2c = I2C(side_str) 88 | if isinstance(side_str, str): # Non-pyb targets may use other than X or Y 89 | self._mpu_i2c = I2C(side_str) 90 | elif isinstance(side_str, int): # WiPY &ESP32 targets 91 | if (side_str == -1): 92 | self._mpu_i2c = I2C(side_str, scl=Pin(dev_pin[0], mode=Pin.OUT), sda=Pin(dev_pin[1], mode=Pin.IN)) # ESP32 93 | else: 94 | self._mpu_i2c = I2C(side_str) # WiPy 95 | elif hasattr(side_str, 'readfrom'): # Soft or hard I2C instance. See issue #3097 96 | self._mpu_i2c = side_str 97 | else: 98 | raise ValueError("Invalid I2C instance") 99 | 100 | if device_addr is None: 101 | devices = set(self._mpu_i2c.scan()) 102 | mpus = devices.intersection(set(self._mpu_addr)) 103 | number_of_mpus = len(mpus) 104 | if number_of_mpus == 0: 105 | raise MPUException("No MPU's detected") 106 | elif number_of_mpus == 1: 107 | self.mpu_addr = mpus.pop() 108 | else: 109 | raise ValueError("Two MPU's detected: must specify a device address") 110 | else: 111 | if device_addr not in (0, 1): 112 | raise ValueError('Device address must be 0 or 1') 113 | self.mpu_addr = self._mpu_addr[device_addr] 114 | 115 | self.chip_id # Test communication by reading chip_id: throws exception on error 116 | # Can communicate with chip. Set it up. 117 | self.wake() # wake it up 118 | self.passthrough = True # Enable mag access from main I2C bus 119 | self.accel_range = 0 # default to highest sensitivity 120 | self.gyro_range = 0 # Likewise for gyro 121 | 122 | # read from device 123 | def _read(self, buf, memaddr, addr): # addr = I2C device address, memaddr = memory location within the I2C device 124 | ''' 125 | Read bytes to pre-allocated buffer Caller traps OSError. 126 | ''' 127 | self._mpu_i2c.readfrom_mem_into(addr, memaddr, buf) 128 | 129 | # write to device 130 | def _write(self, data, memaddr, addr): 131 | ''' 132 | Perform a memory write. Caller should trap OSError. 133 | ''' 134 | self.buf1[0] = data 135 | self._mpu_i2c.writeto_mem(addr, memaddr, self.buf1) 136 | 137 | # wake 138 | def wake(self): 139 | ''' 140 | Wakes the device. 141 | ''' 142 | try: 143 | self._write(0x01, 0x6B, self.mpu_addr) # Use best clock source 144 | except OSError: 145 | raise MPUException(self._I2Cerror) 146 | return 'awake' 147 | 148 | # mode 149 | def sleep(self): 150 | ''' 151 | Sets the device to sleep mode. 152 | ''' 153 | try: 154 | self._write(0x40, 0x6B, self.mpu_addr) 155 | except OSError: 156 | raise MPUException(self._I2Cerror) 157 | return 'asleep' 158 | 159 | # chip_id 160 | @property 161 | def chip_id(self): 162 | ''' 163 | Returns Chip ID 164 | ''' 165 | try: 166 | self._read(self.buf1, 0x75, self.mpu_addr) 167 | except OSError: 168 | raise MPUException(self._I2Cerror) 169 | chip_id = int(self.buf1[0]) 170 | if chip_id != self._chip_id: 171 | raise ValueError('Bad chip ID ({0}!={1}) retrieved: MPU communication failure'.format(chip_id, self._chip_id)) 172 | return chip_id 173 | 174 | @property 175 | def sensors(self): 176 | ''' 177 | returns sensor objects accel, gyro 178 | ''' 179 | return self._accel, self._gyro 180 | 181 | # get temperature 182 | @property 183 | def temperature(self): 184 | ''' 185 | Returns the temperature in degree C. 186 | ''' 187 | try: 188 | self._read(self.buf2, 0x41, self.mpu_addr) 189 | except OSError: 190 | raise MPUException(self._I2Cerror) 191 | return bytes_toint(self.buf2[0], self.buf2[1])/340 + 35 # I think 192 | 193 | # passthrough 194 | @property 195 | def passthrough(self): 196 | ''' 197 | Returns passthrough mode True or False 198 | ''' 199 | try: 200 | self._read(self.buf1, 0x37, self.mpu_addr) 201 | return self.buf1[0] & 0x02 > 0 202 | except OSError: 203 | raise MPUException(self._I2Cerror) 204 | 205 | @passthrough.setter 206 | def passthrough(self, mode): 207 | ''' 208 | Sets passthrough mode True or False 209 | ''' 210 | if type(mode) is bool: 211 | val = 2 if mode else 0 212 | try: 213 | self._write(val, 0x37, self.mpu_addr) # I think this is right. 214 | self._write(0x00, 0x6A, self.mpu_addr) 215 | except OSError: 216 | raise MPUException(self._I2Cerror) 217 | else: 218 | raise ValueError('pass either True or False') 219 | 220 | # sample rate. Not sure why you'd ever want to reduce this from the default. 221 | @property 222 | def sample_rate(self): 223 | ''' 224 | Get sample rate as per Register Map document section 4.4 225 | SAMPLE_RATE= Internal_Sample_Rate / (1 + rate) 226 | default rate is zero i.e. sample at internal rate. 227 | ''' 228 | try: 229 | self._read(self.buf1, 0x19, self.mpu_addr) 230 | return self.buf1[0] 231 | except OSError: 232 | raise MPUException(self._I2Cerror) 233 | 234 | @sample_rate.setter 235 | def sample_rate(self, rate): 236 | ''' 237 | Set sample rate as per Register Map document section 4.4 238 | ''' 239 | if rate < 0 or rate > 255: 240 | raise ValueError("Rate must be in range 0-255") 241 | try: 242 | self._write(rate, 0x19, self.mpu_addr) 243 | except OSError: 244 | raise MPUException(self._I2Cerror) 245 | 246 | # Low pass filters. Using the filter_range property of the MPU9250 is 247 | # harmless but gyro_filter_range is preferred and offers an extra setting. 248 | @property 249 | def filter_range(self): 250 | ''' 251 | Returns the gyro and temperature sensor low pass filter cutoff frequency 252 | Pass: 0 1 2 3 4 5 6 253 | Cutoff (Hz): 250 184 92 41 20 10 5 254 | Sample rate (KHz): 8 1 1 1 1 1 1 255 | ''' 256 | try: 257 | self._read(self.buf1, 0x1A, self.mpu_addr) 258 | res = self.buf1[0] & 7 259 | except OSError: 260 | raise MPUException(self._I2Cerror) 261 | return res 262 | 263 | @filter_range.setter 264 | def filter_range(self, filt): 265 | ''' 266 | Sets the gyro and temperature sensor low pass filter cutoff frequency 267 | Pass: 0 1 2 3 4 5 6 268 | Cutoff (Hz): 250 184 92 41 20 10 5 269 | Sample rate (KHz): 8 1 1 1 1 1 1 270 | ''' 271 | # set range 272 | if filt in range(7): 273 | try: 274 | self._write(filt, 0x1A, self.mpu_addr) 275 | except OSError: 276 | raise MPUException(self._I2Cerror) 277 | else: 278 | raise ValueError('Filter coefficient must be between 0 and 6') 279 | 280 | # accelerometer range 281 | @property 282 | def accel_range(self): 283 | ''' 284 | Accelerometer range 285 | Value: 0 1 2 3 286 | for range +/-: 2 4 8 16 g 287 | ''' 288 | try: 289 | self._read(self.buf1, 0x1C, self.mpu_addr) 290 | ari = self.buf1[0]//8 291 | except OSError: 292 | raise MPUException(self._I2Cerror) 293 | return ari 294 | 295 | @accel_range.setter 296 | def accel_range(self, accel_range): 297 | ''' 298 | Set accelerometer range 299 | Pass: 0 1 2 3 300 | for range +/-: 2 4 8 16 g 301 | ''' 302 | ar_bytes = (0x00, 0x08, 0x10, 0x18) 303 | if accel_range in range(len(ar_bytes)): 304 | try: 305 | self._write(ar_bytes[accel_range], 0x1C, self.mpu_addr) 306 | except OSError: 307 | raise MPUException(self._I2Cerror) 308 | else: 309 | raise ValueError('accel_range can only be 0, 1, 2 or 3') 310 | 311 | # gyroscope range 312 | @property 313 | def gyro_range(self): 314 | ''' 315 | Gyroscope range 316 | Value: 0 1 2 3 317 | for range +/-: 250 500 1000 2000 degrees/second 318 | ''' 319 | # set range 320 | try: 321 | self._read(self.buf1, 0x1B, self.mpu_addr) 322 | gri = self.buf1[0]//8 323 | except OSError: 324 | raise MPUException(self._I2Cerror) 325 | return gri 326 | 327 | @gyro_range.setter 328 | def gyro_range(self, gyro_range): 329 | ''' 330 | Set gyroscope range 331 | Pass: 0 1 2 3 332 | for range +/-: 250 500 1000 2000 degrees/second 333 | ''' 334 | gr_bytes = (0x00, 0x08, 0x10, 0x18) 335 | if gyro_range in range(len(gr_bytes)): 336 | try: 337 | self._write(gr_bytes[gyro_range], 0x1B, self.mpu_addr) # Sets fchoice = b11 which enables filter 338 | except OSError: 339 | raise MPUException(self._I2Cerror) 340 | else: 341 | raise ValueError('gyro_range can only be 0, 1, 2 or 3') 342 | 343 | # Accelerometer 344 | @property 345 | def accel(self): 346 | ''' 347 | Acceleremoter object 348 | ''' 349 | return self._accel 350 | 351 | def _accel_callback(self): 352 | ''' 353 | Update accelerometer Vector3d object 354 | ''' 355 | try: 356 | self._read(self.buf6, 0x3B, self.mpu_addr) 357 | except OSError: 358 | raise MPUException(self._I2Cerror) 359 | self._accel._ivector[0] = bytes_toint(self.buf6[0], self.buf6[1]) 360 | self._accel._ivector[1] = bytes_toint(self.buf6[2], self.buf6[3]) 361 | self._accel._ivector[2] = bytes_toint(self.buf6[4], self.buf6[5]) 362 | scale = (16384, 8192, 4096, 2048) 363 | self._accel._vector[0] = self._accel._ivector[0]/scale[self.accel_range] 364 | self._accel._vector[1] = self._accel._ivector[1]/scale[self.accel_range] 365 | self._accel._vector[2] = self._accel._ivector[2]/scale[self.accel_range] 366 | 367 | def get_accel_irq(self): 368 | ''' 369 | For use in interrupt handlers. Sets self._accel._ivector[] to signed 370 | unscaled integer accelerometer values 371 | ''' 372 | self._read(self.buf6, 0x3B, self.mpu_addr) 373 | self._accel._ivector[0] = bytes_toint(self.buf6[0], self.buf6[1]) 374 | self._accel._ivector[1] = bytes_toint(self.buf6[2], self.buf6[3]) 375 | self._accel._ivector[2] = bytes_toint(self.buf6[4], self.buf6[5]) 376 | 377 | # Gyro 378 | @property 379 | def gyro(self): 380 | ''' 381 | Gyroscope object 382 | ''' 383 | return self._gyro 384 | 385 | def _gyro_callback(self): 386 | ''' 387 | Update gyroscope Vector3d object 388 | ''' 389 | try: 390 | self._read(self.buf6, 0x43, self.mpu_addr) 391 | except OSError: 392 | raise MPUException(self._I2Cerror) 393 | self._gyro._ivector[0] = bytes_toint(self.buf6[0], self.buf6[1]) 394 | self._gyro._ivector[1] = bytes_toint(self.buf6[2], self.buf6[3]) 395 | self._gyro._ivector[2] = bytes_toint(self.buf6[4], self.buf6[5]) 396 | scale = (131, 65.5, 32.8, 16.4) 397 | self._gyro._vector[0] = self._gyro._ivector[0]/scale[self.gyro_range] 398 | self._gyro._vector[1] = self._gyro._ivector[1]/scale[self.gyro_range] 399 | self._gyro._vector[2] = self._gyro._ivector[2]/scale[self.gyro_range] 400 | 401 | def get_gyro_irq(self): 402 | ''' 403 | For use in interrupt handlers. Sets self._gyro._ivector[] to signed 404 | unscaled integer gyro values. Error trapping disallowed. 405 | ''' 406 | self._read(self.buf6, 0x43, self.mpu_addr) 407 | self._gyro._ivector[0] = bytes_toint(self.buf6[0], self.buf6[1]) 408 | self._gyro._ivector[1] = bytes_toint(self.buf6[2], self.buf6[3]) 409 | self._gyro._ivector[2] = bytes_toint(self.buf6[4], self.buf6[5]) 410 | -------------------------------------------------------------------------------- /mpu9250.py: -------------------------------------------------------------------------------- 1 | # mpu9250.py MicroPython driver for the InvenSense MPU9250 inertial measurement unit 2 | # Authors Peter Hinch, Sebastian Plamauer 3 | # V0.5 17th June 2015 4 | 5 | ''' 6 | mpu9250 is a micropython module for the InvenSense MPU9250 sensor. 7 | It measures acceleration, turn rate and the magnetic field in three axis. 8 | 9 | The MIT License (MIT) 10 | Copyright (c) 2014 Sebastian Plamauer, oeplse@gmail.com, Peter Hinch 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | ''' 27 | 28 | from .imu import MPU6050, bytes_toint, MPUException 29 | from .vector3d import Vector3d 30 | 31 | 32 | class MPU9250(MPU6050): 33 | ''' 34 | MPU9250 constructor arguments 35 | 1. side_str 'X' or 'Y' depending on the Pyboard I2C interface being used 36 | 2. optional device_addr 0, 1 depending on the voltage applied to pin AD0 (Drotek default is 1) 37 | if None driver will scan for a device (if one device only is on bus) 38 | 3, 4. transposition, scaling optional 3-tuples allowing for outputs to be based on vehicle 39 | coordinates rather than those of the sensor itself. See readme. 40 | ''' 41 | 42 | _mag_addr = 12 # Magnetometer address 43 | _chip_id = 113 44 | 45 | def __init__(self, side_str, dev_pin=(15, 4), device_addr=None, transposition=(0, 1, 2), scaling=(1, 1, 1)): 46 | 47 | super().__init__(side_str, dev_pin, device_addr, transposition, scaling) 48 | self._mag = Vector3d(transposition, scaling, self._mag_callback) 49 | self.accel_filter_range = 0 # fast filtered response 50 | self.gyro_filter_range = 0 51 | self._mag_stale_count = 0 # MPU9250 count of consecutive reads where old data was returned 52 | self.mag_correction = self._magsetup() # 16 bit, 100Hz update.Return correction factors. 53 | self._mag_callback() # Seems neccessary to kick the mag off else 1st reading is zero (?) 54 | 55 | def enable_irq_mode(self, level=0x22, freq=0x03): 56 | self.filter_range = 1 # set accel lpf to 184Hz 57 | self._write(0x40, 0x38, self.mpu_addr) # enable motion interrupt 58 | self._write(0xC0, 0x69, self.mpu_addr) # enable accel hardware intelligence 59 | self._write(level, 0x1F, self.mpu_addr) # motion threshold: 1~255 LSBs (0~1020mg) 60 | self._write(freq, 0x1E, self.mpu_addr) # motion frequency: [3:0] = 0.24Hz ~ 500Hz 61 | self._write(0x21, 0x6B, self.mpu_addr) # enable cycle mode (Accel Low Power Mode) 62 | 63 | def disable_irq_mode(self): 64 | self._write(0x00, 0x69, self.mpu_addr) # disable accel hardware intelligence 65 | self._write(0x01, 0x6B, self.mpu_addr) # enable sensor 66 | 67 | 68 | @property 69 | def sensors(self): 70 | ''' 71 | returns sensor objects accel, gyro and mag 72 | ''' 73 | return self._accel, self._gyro, self._mag 74 | 75 | # get temperature 76 | @property 77 | def temperature(self): 78 | ''' 79 | Returns the temperature in degree C. 80 | ''' 81 | try: 82 | self._read(self.buf2, 0x41, self.mpu_addr) 83 | except OSError: 84 | raise MPUException(self._I2Cerror) 85 | return bytes_toint(self.buf2[0], self.buf2[1])/333.87 + 21 # I think 86 | 87 | # Low pass filters 88 | @property 89 | def gyro_filter_range(self): 90 | ''' 91 | Returns the gyro and temperature sensor low pass filter cutoff frequency 92 | Pass: 0 1 2 3 4 5 6 7 93 | Cutoff (Hz): 250 184 92 41 20 10 5 3600 94 | Sample rate (KHz): 8 1 1 1 1 1 1 8 95 | ''' 96 | try: 97 | self._read(self.buf1, 0x1A, self.mpu_addr) 98 | res = self.buf1[0] & 7 99 | except OSError: 100 | raise MPUException(self._I2Cerror) 101 | return res 102 | 103 | @gyro_filter_range.setter 104 | def gyro_filter_range(self, filt): 105 | ''' 106 | Sets the gyro and temperature sensor low pass filter cutoff frequency 107 | Pass: 0 1 2 3 4 5 6 7 108 | Cutoff (Hz): 250 184 92 41 20 10 5 3600 109 | Sample rate (KHz): 8 1 1 1 1 1 1 8 110 | ''' 111 | if filt in range(8): 112 | try: 113 | self._write(filt, 0x1A, self.mpu_addr) 114 | except OSError: 115 | raise MPUException(self._I2Cerror) 116 | else: 117 | raise ValueError('Filter coefficient must be between 0 and 7') 118 | 119 | @property 120 | def accel_filter_range(self): 121 | ''' 122 | Returns the accel low pass filter cutoff frequency 123 | Pass: 0 1 2 3 4 5 6 7 BEWARE 7 doesn't work on device I tried. 124 | Cutoff (Hz): 460 184 92 41 20 10 5 460 125 | Sample rate (KHz): 1 1 1 1 1 1 1 1 126 | ''' 127 | try: 128 | self._read(self.buf1, 0x1D, self.mpu_addr) 129 | res = self.buf1[0] & 7 130 | except OSError: 131 | raise MPUException(self._I2Cerror) 132 | return res 133 | 134 | @accel_filter_range.setter 135 | def accel_filter_range(self, filt): 136 | ''' 137 | Sets the accel low pass filter cutoff frequency 138 | Pass: 0 1 2 3 4 5 6 7 BEWARE 7 doesn't work on device I tried. 139 | Cutoff (Hz): 460 184 92 41 20 10 5 460 140 | Sample rate (KHz): 1 1 1 1 1 1 1 1 141 | ''' 142 | if filt in range(8): 143 | try: 144 | self._write(filt, 0x1D, self.mpu_addr) 145 | except OSError: 146 | raise MPUException(self._I2Cerror) 147 | else: 148 | raise ValueError('Filter coefficient must be between 0 and 7') 149 | 150 | def _magsetup(self): # mode 2 100Hz continuous reads, 16 bit 151 | ''' 152 | Magnetometer initialisation: use 16 bit continuous mode. 153 | Mode 1 is 8Hz mode 2 is 100Hz repetition 154 | returns correction values 155 | ''' 156 | try: 157 | self._write(0x0F, 0x0A, self._mag_addr) # fuse ROM access mode 158 | self._read(self.buf3, 0x10, self._mag_addr) # Correction values 159 | self._write(0, 0x0A, self._mag_addr) # Power down mode (AK8963 manual 6.4.6) 160 | self._write(0x16, 0x0A, self._mag_addr) # 16 bit (0.15uT/LSB not 0.015), mode 2 161 | except OSError: 162 | raise MPUException(self._I2Cerror) 163 | mag_x = (0.5*(self.buf3[0] - 128))/128 + 1 164 | mag_y = (0.5*(self.buf3[1] - 128))/128 + 1 165 | mag_z = (0.5*(self.buf3[2] - 128))/128 + 1 166 | return (mag_x, mag_y, mag_z) 167 | 168 | @property 169 | def mag(self): 170 | ''' 171 | Magnetomerte object 172 | ''' 173 | return self._mag 174 | 175 | def _mag_callback(self): 176 | ''' 177 | Update magnetometer Vector3d object (if data available) 178 | ''' 179 | try: # If read fails, returns last valid data and 180 | self._read(self.buf1, 0x02, self._mag_addr) # increments mag_stale_count 181 | if self.buf1[0] & 1 == 0: 182 | return self._mag # Data not ready: return last value 183 | self._read(self.buf6, 0x03, self._mag_addr) 184 | self._read(self.buf1, 0x09, self._mag_addr) 185 | except OSError: 186 | raise MPUException(self._I2Cerror) 187 | if self.buf1[0] & 0x08 > 0: # An overflow has occurred 188 | self._mag_stale_count += 1 # Error conditions retain last good value 189 | return # user should check for ever increasing stale_counts 190 | self._mag._ivector[1] = bytes_toint(self.buf6[1], self.buf6[0]) # Note axis twiddling and little endian 191 | self._mag._ivector[0] = bytes_toint(self.buf6[3], self.buf6[2]) 192 | self._mag._ivector[2] = -bytes_toint(self.buf6[5], self.buf6[4]) 193 | scale = 0.15 # scale is 0.15uT/LSB 194 | self._mag._vector[0] = self._mag._ivector[0]*self.mag_correction[0]*scale 195 | self._mag._vector[1] = self._mag._ivector[1]*self.mag_correction[1]*scale 196 | self._mag._vector[2] = self._mag._ivector[2]*self.mag_correction[2]*scale 197 | self._mag_stale_count = 0 198 | 199 | @property 200 | def mag_stale_count(self): 201 | ''' 202 | Number of consecutive times where old data was returned 203 | ''' 204 | return self._mag_stale_count 205 | 206 | def get_mag_irq(self): 207 | ''' 208 | Uncorrected values because floating point uses heap 209 | ''' 210 | self._read(self.buf1, 0x02, self._mag_addr) 211 | if self.buf1[0] == 1: # Data is ready 212 | self._read(self.buf6, 0x03, self._mag_addr) 213 | self._read(self.buf1, 0x09, self._mag_addr) # Mandatory status2 read 214 | self._mag._ivector[1] = 0 215 | if self.buf1[0] & 0x08 == 0: # No overflow has occurred 216 | self._mag._ivector[1] = bytes_toint(self.buf6[1], self.buf6[0]) 217 | self._mag._ivector[0] = bytes_toint(self.buf6[3], self.buf6[2]) 218 | self._mag._ivector[2] = -bytes_toint(self.buf6[5], self.buf6[4]) 219 | -------------------------------------------------------------------------------- /pmsa003.py: -------------------------------------------------------------------------------- 1 | 2 | import machine 3 | import utime 4 | 5 | epoch_offset = 946684800 6 | 7 | class PMSA003: 8 | def __init__(self, uart, pins): 9 | 10 | self._uart = uart 11 | self._pins = pins 12 | 13 | self._set = machine.Pin(self._pins["set"], machine.Pin.OUT, value=0) 14 | self._rst = machine.Pin(self._pins["rst"], machine.Pin.OUT, value=0) 15 | self._uart.init(tx=self._pins["tx"], rx=self._pins["rx"]) 16 | self.power_off() 17 | 18 | def wake_up(self): 19 | self._set(True) 20 | self._rst(True) 21 | # sleep for 7 seconds to initialize the sensor properly 22 | self._set_normal() 23 | utime.sleep_ms(7000) 24 | # warning init 25 | for idx in range(10): 26 | data = self.measurements 27 | utime.sleep_ms(500) 28 | 29 | def _set_idle(self): 30 | idelcmd = b'\x42\x4d\xe4\x00\x00\x01\x73' 31 | ary = bytearray(idelcmd) 32 | self._uart.write(ary) 33 | 34 | def _set_normal(self): 35 | normalcmd = b'\x42\x4d\xe4\x00\x01\x01\x74' 36 | ary = bytearray(normalcmd) 37 | self._uart.write(ary) 38 | 39 | def power_off(self): 40 | self._set_idle() 41 | self._set(False) 42 | self._rst(False) 43 | 44 | def reset(self): 45 | self._rst(False) 46 | utime.sleep_ms(2000) 47 | self._rst(True) 48 | 49 | @property 50 | def measurements(self): 51 | # flush the buffer to read fresh data 52 | ret_data = None 53 | self._wait_for_data(32) 54 | 55 | while self._uart.read(1) != b'\x42': 56 | machine.idle() 57 | 58 | if self._uart.read(1) == b'\x4D': 59 | self._wait_for_data(30) 60 | try: 61 | self._data = self._uart.read(30) 62 | if self._data: 63 | ret_data = self._PMdata() 64 | except ValueError as e: 65 | print('error reading frame: {}'.format(e.message)) 66 | pass 67 | 68 | return ret_data 69 | 70 | def _wait_for_data(self, byte_count): 71 | u = self._uart.any() 72 | while u < byte_count: 73 | u = self._uart.any() 74 | # 32*8*1000/9600 (32 bytes @9600kbps) 75 | # but let's assume byte is 10 bits to skip complex math 76 | utime.sleep_ms(10) 77 | 78 | def _PMdata(self): 79 | d = {} 80 | check = False 81 | # check data 82 | control_sum = 0x42 + 0x4d 83 | for b in range(len(self._data)-2): 84 | control_sum += self._data[b] 85 | 86 | control_sum_data = self._data[28] * 256 + self._data[29] 87 | print() 88 | if control_sum == control_sum_data: 89 | check = True 90 | 91 | d['time'] = utime.time() + epoch_offset 92 | d['cpm10'] = self._data[4] * 256 + self._data[5] 93 | d['cpm25'] = self._data[6] * 256 + self._data[7] 94 | d['cpm100'] = self._data[8] * 256 + self._data[9] 95 | d['apm10'] = self._data[10] * 256 + self._data[11] 96 | d['apm25'] = self._data[12] * 256 + self._data[13] 97 | d['apm100'] = self._data[14] * 256 + self._data[15] 98 | 99 | return [check, d] -------------------------------------------------------------------------------- /sdcard.py: -------------------------------------------------------------------------------- 1 | """ 2 | MicroPython driver for SD cards using SPI bus. 3 | 4 | Requires an SPI bus and a CS pin. Provides readblocks and writeblocks 5 | methods so the device can be mounted as a filesystem. 6 | 7 | Example usage on pyboard: 8 | 9 | import pyb, sdcard, os 10 | sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5) 11 | pyb.mount(sd, '/sd2') 12 | os.listdir('/') 13 | 14 | Example usage on ESP8266: 15 | 16 | import machine, sdcard, os 17 | sd = sdcard.SDCard(machine.SPI(1), machine.Pin(15)) 18 | os.mount(sd, '/sd') 19 | os.listdir('/') 20 | 21 | """ 22 | 23 | from micropython import const 24 | from machine import Pin 25 | import time 26 | 27 | 28 | _CMD_TIMEOUT = const(100) 29 | 30 | _R1_IDLE_STATE = const(1 << 0) 31 | #R1_ERASE_RESET = const(1 << 1) 32 | _R1_ILLEGAL_COMMAND = const(1 << 2) 33 | #R1_COM_CRC_ERROR = const(1 << 3) 34 | #R1_ERASE_SEQUENCE_ERROR = const(1 << 4) 35 | #R1_ADDRESS_ERROR = const(1 << 5) 36 | #R1_PARAMETER_ERROR = const(1 << 6) 37 | _TOKEN_CMD25 = const(0xfc) 38 | _TOKEN_STOP_TRAN = const(0xfd) 39 | _TOKEN_DATA = const(0xfe) 40 | 41 | 42 | class SDCard: 43 | def __init__(self, spi, cs): 44 | self.spi = spi 45 | self.cs = cs 46 | 47 | self.cmdbuf = bytearray(6) 48 | self.dummybuf = bytearray(512) 49 | self.tokenbuf = bytearray(1) 50 | for i in range(512): 51 | self.dummybuf[i] = 0xff 52 | self.dummybuf_memoryview = memoryview(self.dummybuf) 53 | 54 | # initialise the card 55 | self.init_card() 56 | 57 | def init_spi(self, baudrate): 58 | try: 59 | master = self.spi.MASTER 60 | except AttributeError: 61 | # on ESP8266 62 | self.spi.init(baudrate=baudrate, phase=0, polarity=0) 63 | else: 64 | # on pyboard 65 | self.spi.init(master, baudrate=baudrate, phase=0, polarity=0) 66 | 67 | def init_card(self): 68 | # init CS pin 69 | self.cs.init(self.cs.OUT, value=1) 70 | 71 | # init SPI bus; use low data rate for initialisation 72 | self.init_spi(10000000) 73 | 74 | # clock card at least 100 cycles with cs high 75 | for i in range(16): 76 | self.spi.write(b'\xff') 77 | 78 | # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts) 79 | for _ in range(5): 80 | if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE: 81 | break 82 | else: 83 | raise OSError("no SD card") 84 | 85 | # CMD8: determine card version 86 | r = self.cmd(8, 0x01aa, 0x87, 4) 87 | if r == _R1_IDLE_STATE: 88 | self.init_card_v2() 89 | elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND): 90 | self.init_card_v1() 91 | else: 92 | raise OSError("couldn't determine SD card version") 93 | 94 | # get the number of sectors 95 | # CMD9: response R2 (R1 byte + 16-byte block read) 96 | if self.cmd(9, 0, 0, 0, False) != 0: 97 | raise OSError("no response from SD card") 98 | csd = bytearray(16) 99 | self.readinto(csd) 100 | if csd[0] & 0xc0 == 0x40: # CSD version 2.0 101 | self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024 102 | elif csd[0] & 0xc0 == 0x00: # CSD version 1.0 (old, <=2GB) 103 | c_size = csd[6] & 0b11 | csd[7] << 2 | (csd[8] & 0b11000000) << 4 104 | c_size_mult = ((csd[9] & 0b11) << 1) | csd[10] >> 7 105 | self.sectors = (c_size + 1) * (2 ** (c_size_mult + 2)) 106 | else: 107 | raise OSError("SD card CSD format not supported") 108 | #print('sectors', self.sectors) 109 | 110 | # CMD16: set block length to 512 bytes 111 | if self.cmd(16, 512, 0) != 0: 112 | raise OSError("can't set 512 block size") 113 | 114 | # set to high data rate now that it's initialised 115 | self.init_spi(1320000) 116 | 117 | def init_card_v1(self): 118 | for i in range(_CMD_TIMEOUT): 119 | self.cmd(55, 0, 0) 120 | if self.cmd(41, 0, 0) == 0: 121 | self.cdv = 512 122 | #print("[SDCard] v1 card") 123 | return 124 | raise OSError("timeout waiting for v1 card") 125 | 126 | def init_card_v2(self): 127 | for i in range(_CMD_TIMEOUT): 128 | time.sleep_ms(50) 129 | self.cmd(58, 0, 0, 4) 130 | self.cmd(55, 0, 0) 131 | if self.cmd(41, 0x40000000, 0) == 0: 132 | self.cmd(58, 0, 0, 4) 133 | self.cdv = 1 134 | #print("[SDCard] v2 card") 135 | return 136 | raise OSError("timeout waiting for v2 card") 137 | 138 | def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False): 139 | self.cs(0) 140 | 141 | # create and send the command 142 | buf = self.cmdbuf 143 | buf[0] = 0x40 | cmd 144 | buf[1] = arg >> 24 145 | buf[2] = arg >> 16 146 | buf[3] = arg >> 8 147 | buf[4] = arg 148 | buf[5] = crc 149 | self.spi.write(buf) 150 | 151 | if skip1: 152 | self.spi.readinto(self.tokenbuf, 0xff) 153 | 154 | # wait for the response (response[7] == 0) 155 | for i in range(_CMD_TIMEOUT): 156 | self.spi.readinto(self.tokenbuf, 0xff) 157 | response = self.tokenbuf[0] 158 | if not (response & 0x80): 159 | # this could be a big-endian integer that we are getting here 160 | for j in range(final): 161 | self.spi.write(b'\xff') 162 | if release: 163 | self.cs(1) 164 | self.spi.write(b'\xff') 165 | return response 166 | 167 | # timeout 168 | self.cs(1) 169 | self.spi.write(b'\xff') 170 | return -1 171 | 172 | def readinto(self, buf): 173 | self.cs(0) 174 | 175 | # read until start byte (0xff) 176 | while True: 177 | self.spi.readinto(self.tokenbuf, 0xff) 178 | if self.tokenbuf[0] == _TOKEN_DATA: 179 | break 180 | 181 | # read data 182 | mv = self.dummybuf_memoryview 183 | if len(buf) != len(mv): 184 | mv = mv[:len(buf)] 185 | self.spi.write_readinto(mv, buf) 186 | 187 | # read checksum 188 | self.spi.write(b'\xff') 189 | self.spi.write(b'\xff') 190 | 191 | self.cs(1) 192 | self.spi.write(b'\xff') 193 | 194 | def write(self, token, buf): 195 | self.cs(0) 196 | 197 | # send: start of block, data, checksum 198 | self.spi.read(1, token) 199 | self.spi.write(buf) 200 | self.spi.write(b'\xff') 201 | self.spi.write(b'\xff') 202 | 203 | # check the response 204 | if (self.spi.read(1, 0xff)[0] & 0x1f) != 0x05: 205 | self.cs(1) 206 | self.spi.write(b'\xff') 207 | return 208 | 209 | # wait for write to finish 210 | while self.spi.read(1, 0xff)[0] == 0: 211 | pass 212 | 213 | self.cs(1) 214 | self.spi.write(b'\xff') 215 | 216 | def write_token(self, token): 217 | self.cs(0) 218 | self.spi.read(1, token) 219 | self.spi.write(b'\xff') 220 | # wait for write to finish 221 | while self.spi.read(1, 0xff)[0] == 0x00: 222 | pass 223 | 224 | self.cs(1) 225 | self.spi.write(b'\xff') 226 | 227 | def readblocks(self, block_num, buf): 228 | nblocks = len(buf) // 512 229 | assert nblocks and not len(buf) % 512, 'Buffer length is invalid' 230 | if nblocks == 1: 231 | # CMD17: set read address for single block 232 | if self.cmd(17, block_num * self.cdv, 0, release=False) != 0: 233 | # release the card 234 | self.cs(1) 235 | raise OSError(5) # EIO 236 | # receive the data and release card 237 | self.readinto(buf) 238 | else: 239 | # CMD18: set read address for multiple blocks 240 | if self.cmd(18, block_num * self.cdv, 0, release=False) != 0: 241 | # release the card 242 | self.cs(1) 243 | raise OSError(5) # EIO 244 | offset = 0 245 | mv = memoryview(buf) 246 | while nblocks: 247 | # receive the data and release card 248 | self.readinto(mv[offset : offset + 512]) 249 | offset += 512 250 | nblocks -= 1 251 | if self.cmd(12, 0, 0xff, skip1=True): 252 | raise OSError(5) # EIO 253 | 254 | def writeblocks(self, block_num, buf): 255 | nblocks, err = divmod(len(buf), 512) 256 | assert nblocks and not err, 'Buffer length is invalid' 257 | if nblocks == 1: 258 | # CMD24: set write address for single block 259 | if self.cmd(24, block_num * self.cdv, 0) != 0: 260 | raise OSError(5) # EIO 261 | 262 | # send the data 263 | self.write(_TOKEN_DATA, buf) 264 | else: 265 | # CMD25: set write address for first block 266 | if self.cmd(25, block_num * self.cdv, 0) != 0: 267 | raise OSError(5) # EIO 268 | # send the data 269 | offset = 0 270 | mv = memoryview(buf) 271 | while nblocks: 272 | self.write(_TOKEN_CMD25, mv[offset : offset + 512]) 273 | offset += 512 274 | nblocks -= 1 275 | self.write_token(_TOKEN_STOP_TRAN) 276 | 277 | def ioctl(self, op, arg): 278 | if op == 4: # get number of blocks 279 | return self.sectors 280 | -------------------------------------------------------------------------------- /ssd1306.py: -------------------------------------------------------------------------------- 1 | # MicroPython SSD1306 OLED driver, I2C and SPI interfaces 2 | from micropython import const 3 | import time 4 | import framebuf 5 | 6 | 7 | # register definitions 8 | SET_CONTRAST = const(0x81) 9 | SET_ENTIRE_ON = const(0xa4) 10 | SET_NORM_INV = const(0xa6) 11 | SET_DISP = const(0xae) 12 | SET_MEM_ADDR = const(0x20) 13 | SET_COL_ADDR = const(0x21) 14 | SET_PAGE_ADDR = const(0x22) 15 | SET_DISP_START_LINE = const(0x40) 16 | SET_SEG_REMAP = const(0xa0) 17 | SET_MUX_RATIO = const(0xa8) 18 | SET_COM_OUT_DIR = const(0xc0) 19 | SET_DISP_OFFSET = const(0xd3) 20 | SET_COM_PIN_CFG = const(0xda) 21 | SET_DISP_CLK_DIV = const(0xd5) 22 | SET_PRECHARGE = const(0xd9) 23 | SET_VCOM_DESEL = const(0xdb) 24 | SET_CHARGE_PUMP = const(0x8d) 25 | 26 | 27 | class SSD1306: 28 | def __init__(self, width, height, external_vcc): 29 | self.width = width 30 | self.height = height 31 | self.external_vcc = external_vcc 32 | self.pages = self.height // 8 33 | self.buffer = bytearray(self.pages * self.width) 34 | fb = framebuf.FrameBuffer(self.buffer, self.width, self.height, framebuf.MONO_VLSB) 35 | self.framebuf = fb 36 | # Provide methods for accessing FrameBuffer graphics primitives. This is a 37 | # workround because inheritance from a native class is currently unsupported. 38 | # http://docs.micropython.org/en/latest/pyboard/library/framebuf.html 39 | self.fill = fb.fill 40 | self.pixel = fb.pixel 41 | self.hline = fb.hline 42 | self.vline = fb.vline 43 | self.line = fb.line 44 | self.rect = fb.rect 45 | self.fill_rect = fb.fill_rect 46 | self.text = fb.text 47 | self.scroll = fb.scroll 48 | self.blit = fb.blit 49 | self.poweron() 50 | self.init_display() 51 | 52 | def init_display(self): 53 | for cmd in ( 54 | SET_DISP | 0x00, # off 55 | # address setting 56 | SET_MEM_ADDR, 0x00, # horizontal 57 | # resolution and layout 58 | SET_DISP_START_LINE | 0x00, 59 | SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 60 | SET_MUX_RATIO, self.height - 1, 61 | SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 62 | SET_DISP_OFFSET, 0x00, 63 | SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12, 64 | # timing and driving scheme 65 | SET_DISP_CLK_DIV, 0x80, 66 | SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1, 67 | SET_VCOM_DESEL, 0x30, # 0.83*Vcc 68 | # display 69 | SET_CONTRAST, 0xff, # maximum 70 | SET_ENTIRE_ON, # output follows RAM contents 71 | SET_NORM_INV, # not inverted 72 | # charge pump 73 | SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14, 74 | SET_DISP | 0x01): # on 75 | self.write_cmd(cmd) 76 | self.fill(0) 77 | self.show() 78 | 79 | def poweroff(self): 80 | self.write_cmd(SET_DISP | 0x00) 81 | 82 | def contrast(self, contrast): 83 | self.write_cmd(SET_CONTRAST) 84 | self.write_cmd(contrast) 85 | 86 | def invert(self, invert): 87 | self.write_cmd(SET_NORM_INV | (invert & 1)) 88 | 89 | def show(self): 90 | x0 = 0 91 | x1 = self.width - 1 92 | if self.width == 64: 93 | # displays with width of 64 pixels are shifted by 32 94 | x0 += 32 95 | x1 += 32 96 | self.write_cmd(SET_COL_ADDR) 97 | self.write_cmd(x0) 98 | self.write_cmd(x1) 99 | self.write_cmd(SET_PAGE_ADDR) 100 | self.write_cmd(0) 101 | self.write_cmd(self.pages - 1) 102 | self.write_data(self.buffer) 103 | 104 | 105 | class SSD1306_I2C(SSD1306): 106 | def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False): 107 | self.i2c = i2c 108 | self.addr = addr 109 | self.temp = bytearray(2) 110 | super().__init__(width, height, external_vcc) 111 | 112 | def write_cmd(self, cmd): 113 | self.temp[0] = 0x80 # Co=1, D/C#=0 114 | self.temp[1] = cmd 115 | self.i2c.writeto(self.addr, self.temp) 116 | 117 | def write_data(self, buf): 118 | self.temp[0] = self.addr << 1 119 | self.temp[1] = 0x40 # Co=0, D/C#=1 120 | self.i2c.start() 121 | self.i2c.write(self.temp) 122 | self.i2c.write(buf) 123 | self.i2c.stop() 124 | 125 | def poweron(self): 126 | pass 127 | 128 | 129 | class SSD1306_SPI(SSD1306): 130 | def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): 131 | self.rate = 10 * 1024 * 1024 132 | dc.init(dc.OUT, value=0) 133 | res.init(res.OUT, value=0) 134 | cs.init(cs.OUT, value=1) 135 | self.spi = spi 136 | self.dc = dc 137 | self.res = res 138 | self.cs = cs 139 | super().__init__(width, height, external_vcc) 140 | 141 | def write_cmd(self, cmd): 142 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 143 | self.cs(1) 144 | self.dc(0) 145 | self.cs(0) 146 | self.spi.write(bytearray([cmd])) 147 | self.cs(1) 148 | 149 | def write_data(self, buf): 150 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 151 | self.cs(1) 152 | self.dc(1) 153 | self.cs(0) 154 | self.spi.write(buf) 155 | self.cs(1) 156 | 157 | def poweron(self): 158 | self.res(1) 159 | time.sleep_ms(1) 160 | self.res(0) 161 | time.sleep_ms(10) 162 | self.res(1) 163 | -------------------------------------------------------------------------------- /ssd1306_i2c.py: -------------------------------------------------------------------------------- 1 | # https://learn.adafruit.com/micropython-hardware-ssd1306-oled-display/software 2 | import time 3 | from machine import I2C, Pin 4 | from .ssd1306 import SSD1306_I2C 5 | 6 | class Display: 7 | 8 | def __init__(self, 9 | width = 128, height = 64, 10 | scl_pin_id = 15, sda_pin_id = 4, 11 | freq = 400000): 12 | 13 | self.width = width 14 | self.height = height 15 | self.poweron() 16 | self.i2c = I2C(scl = Pin(scl_pin_id, Pin.OUT), 17 | sda = Pin(sda_pin_id), 18 | freq = freq) 19 | self.display = SSD1306_I2C(width, height, self.i2c) 20 | self.show = self.display.show 21 | 22 | def poweron(self, pin=16): 23 | pin_reset = Pin(pin, mode=Pin.OUT) 24 | pin_reset.value(0) 25 | time.sleep_ms(50) 26 | pin_reset.value(1) 27 | 28 | def poweroff(self, pin=16): 29 | pin_reset = Pin(pin, mode=Pin.OUT) 30 | pin_reset.value(0) 31 | 32 | def clear(self): 33 | self.display.fill(0) 34 | self.display.show() 35 | 36 | 37 | def show_text(self, text, x = 0, y = 0, clear_first = True, show_now = True, hold_seconds = 0): 38 | if clear_first: self.display.fill(0) 39 | self.display.text(text, x, y) 40 | if show_now: 41 | self.display.show() 42 | if hold_seconds > 0: time.sleep(hold_seconds) 43 | 44 | 45 | def wrap(self, text, start_line = 0, 46 | height_per_line = 8, width_per_char = 8, 47 | start_pixel_each_line = 0): 48 | 49 | chars_per_line = self.width//width_per_char 50 | max_lines = self.height//height_per_line - start_line 51 | lines = [(text[chars_per_line*line: chars_per_line*(line+1)], start_pixel_each_line, height_per_line*(line+start_line)) 52 | for line in range(max_lines)] 53 | 54 | return lines 55 | 56 | 57 | def show_text_wrap(self, text, 58 | start_line = 0, height_per_line = 8, width_per_char = 8, start_pixel_each_line = 0, 59 | clear_first = True, show_now = True, hold_seconds = 0): 60 | 61 | if clear_first: self.clear() 62 | 63 | for line, x, y in self.wrap(text, start_line, height_per_line, width_per_char, start_pixel_each_line): 64 | self.show_text(line, x, y, clear_first = False, show_now = False) 65 | 66 | if show_now: 67 | self.display.show() 68 | if hold_seconds > 0: time.sleep(hold_seconds) 69 | 70 | 71 | def show_datetime(self, year, month, day, hour, minute, second): 72 | datetime = [year, month, day, hour, minute, second] 73 | datetime_str = ["{0:0>2}".format(d) for d in datetime] 74 | 75 | self.show_text(text = '-'.join(datetime_str[:3]), 76 | x = 0, y = 0, clear_first = True, show_now = False) 77 | self.show_text(text = ':'.join(datetime_str[3:6]), 78 | x = 0, y = 10, clear_first = False, show_now = True) 79 | 80 | 81 | def show_time(self, year, month, day, hour, minute, second): 82 | self.show_datetime(year, month, day, hour, minute, second) 83 | -------------------------------------------------------------------------------- /stepper.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 LeMaRiva|Tech (Mauro Riva) info@lemariva.com 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | """ 13 | 14 | import utime 15 | from machine import Pin 16 | 17 | class STEPPER: 18 | 19 | def __init__(self, device_config): 20 | self._In1 = Pin(device_config["In1"], Pin.OUT) 21 | self._In2 = Pin(device_config["In2"], Pin.OUT) 22 | self._In3 = Pin(device_config["In3"], Pin.OUT) 23 | self._In4 = Pin(device_config["In4"], Pin.OUT) 24 | self._number_of_steps = device_config["number_of_steps"] + 1 25 | self._max_speed = 60 * 1000 * 1000 / self._number_of_steps / device_config["max_speed"] 26 | self._step_number = 0 27 | self._last_step_time = 0 28 | self.set_speed(device_config["max_speed"]/2) 29 | 30 | def set_speed(self, speed): 31 | self._step_delay = 60 * 1000 * 1000 / self._number_of_steps / speed 32 | if self._step_delay < self._max_speed: 33 | self._step_delay = self._max_speed 34 | 35 | def step_motor(self, step): 36 | if step == 0: #1010 37 | self._In1.value(1) 38 | self._In2.value(0) 39 | self._In3.value(1) 40 | self._In4.value(0) 41 | elif step == 1: #0110 42 | self._In1.value(0) 43 | self._In2.value(1) 44 | self._In3.value(1) 45 | self._In4.value(0) 46 | elif step == 2: #0101 47 | self._In1.value(0) 48 | self._In2.value(1) 49 | self._In3.value(0) 50 | self._In4.value(1) 51 | elif step == 3: #1001 52 | self._In1.value(1) 53 | self._In2.value(0) 54 | self._In3.value(0) 55 | self._In4.value(1) 56 | 57 | def release(self): 58 | self._In1.value(0) 59 | self._In2.value(0) 60 | self._In3.value(0) 61 | self._In4.value(0) 62 | 63 | def step(self, steps_to_move, speed=None, hold=True): 64 | if speed is not None: 65 | self.set_speed(speed) 66 | 67 | steps_left = abs(steps_to_move) 68 | 69 | if steps_to_move > 0: 70 | direction = 1 71 | else: 72 | direction = 0 73 | 74 | while steps_left > 0: 75 | now = utime.ticks_us() 76 | if now - self._last_step_time >= self._step_delay: 77 | self._last_step_time = now 78 | if direction == 1: 79 | self._step_number += 1 80 | 81 | if direction == 0: 82 | if self._step_number == 0: 83 | self._step_number == steps_left 84 | 85 | self._step_number -= 1 86 | 87 | self.step_motor(self._step_number % 4) 88 | steps_left -= 1 89 | 90 | if self._step_number == steps_left: 91 | self._step_number = 0 92 | 93 | if steps_left == 0 and not hold: 94 | self.release() 95 | -------------------------------------------------------------------------------- /sysfont.py: -------------------------------------------------------------------------------- 1 | 2 | #Font used for ST7735 display. 3 | 4 | #Each character uses 5 bytes. 5 | #index using ASCII value * 5. 6 | #Each byte contains a column of pixels. 7 | #The character may be 8 pixels high and 5 wide. 8 | 9 | sysfont = {"Width": 5, "Height": 8, "Start": 0, "End": 254, "Data": bytearray([ 10 | 0x00, 0x00, 0x00, 0x00, 0x00, 11 | 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 12 | 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, 13 | 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 14 | 0x18, 0x3C, 0x7E, 0x3C, 0x18, 15 | 0x1C, 0x57, 0x7D, 0x57, 0x1C, 16 | 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 17 | 0x00, 0x18, 0x3C, 0x18, 0x00, 18 | 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 19 | 0x00, 0x18, 0x24, 0x18, 0x00, 20 | 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 21 | 0x30, 0x48, 0x3A, 0x06, 0x0E, 22 | 0x26, 0x29, 0x79, 0x29, 0x26, 23 | 0x40, 0x7F, 0x05, 0x05, 0x07, 24 | 0x40, 0x7F, 0x05, 0x25, 0x3F, 25 | 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 26 | 0x7F, 0x3E, 0x1C, 0x1C, 0x08, 27 | 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 28 | 0x14, 0x22, 0x7F, 0x22, 0x14, 29 | 0x5F, 0x5F, 0x00, 0x5F, 0x5F, 30 | 0x06, 0x09, 0x7F, 0x01, 0x7F, 31 | 0x00, 0x66, 0x89, 0x95, 0x6A, 32 | 0x60, 0x60, 0x60, 0x60, 0x60, 33 | 0x94, 0xA2, 0xFF, 0xA2, 0x94, 34 | 0x08, 0x04, 0x7E, 0x04, 0x08, 35 | 0x10, 0x20, 0x7E, 0x20, 0x10, 36 | 0x08, 0x08, 0x2A, 0x1C, 0x08, 37 | 0x08, 0x1C, 0x2A, 0x08, 0x08, 38 | 0x1E, 0x10, 0x10, 0x10, 0x10, 39 | 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 40 | 0x30, 0x38, 0x3E, 0x38, 0x30, 41 | 0x06, 0x0E, 0x3E, 0x0E, 0x06, 42 | 0x00, 0x00, 0x00, 0x00, 0x00, 43 | 0x00, 0x00, 0x5F, 0x00, 0x00, 44 | 0x00, 0x07, 0x00, 0x07, 0x00, 45 | 0x14, 0x7F, 0x14, 0x7F, 0x14, 46 | 0x24, 0x2A, 0x7F, 0x2A, 0x12, 47 | 0x23, 0x13, 0x08, 0x64, 0x62, 48 | 0x36, 0x49, 0x56, 0x20, 0x50, 49 | 0x00, 0x08, 0x07, 0x03, 0x00, 50 | 0x00, 0x1C, 0x22, 0x41, 0x00, 51 | 0x00, 0x41, 0x22, 0x1C, 0x00, 52 | 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 53 | 0x08, 0x08, 0x3E, 0x08, 0x08, 54 | 0x00, 0x80, 0x70, 0x30, 0x00, 55 | 0x08, 0x08, 0x08, 0x08, 0x08, 56 | 0x00, 0x00, 0x60, 0x60, 0x00, 57 | 0x20, 0x10, 0x08, 0x04, 0x02, 58 | 0x3E, 0x51, 0x49, 0x45, 0x3E, 59 | 0x00, 0x42, 0x7F, 0x40, 0x00, 60 | 0x72, 0x49, 0x49, 0x49, 0x46, 61 | 0x21, 0x41, 0x49, 0x4D, 0x33, 62 | 0x18, 0x14, 0x12, 0x7F, 0x10, 63 | 0x27, 0x45, 0x45, 0x45, 0x39, 64 | 0x3C, 0x4A, 0x49, 0x49, 0x31, 65 | 0x41, 0x21, 0x11, 0x09, 0x07, 66 | 0x36, 0x49, 0x49, 0x49, 0x36, 67 | 0x46, 0x49, 0x49, 0x29, 0x1E, 68 | 0x00, 0x00, 0x14, 0x00, 0x00, 69 | 0x00, 0x40, 0x34, 0x00, 0x00, 70 | 0x00, 0x08, 0x14, 0x22, 0x41, 71 | 0x14, 0x14, 0x14, 0x14, 0x14, 72 | 0x00, 0x41, 0x22, 0x14, 0x08, 73 | 0x02, 0x01, 0x59, 0x09, 0x06, 74 | 0x3E, 0x41, 0x5D, 0x59, 0x4E, 75 | 0x7C, 0x12, 0x11, 0x12, 0x7C, 76 | 0x7F, 0x49, 0x49, 0x49, 0x36, 77 | 0x3E, 0x41, 0x41, 0x41, 0x22, 78 | 0x7F, 0x41, 0x41, 0x41, 0x3E, 79 | 0x7F, 0x49, 0x49, 0x49, 0x41, 80 | 0x7F, 0x09, 0x09, 0x09, 0x01, 81 | 0x3E, 0x41, 0x41, 0x51, 0x73, 82 | 0x7F, 0x08, 0x08, 0x08, 0x7F, 83 | 0x00, 0x41, 0x7F, 0x41, 0x00, 84 | 0x20, 0x40, 0x41, 0x3F, 0x01, 85 | 0x7F, 0x08, 0x14, 0x22, 0x41, 86 | 0x7F, 0x40, 0x40, 0x40, 0x40, 87 | 0x7F, 0x02, 0x1C, 0x02, 0x7F, 88 | 0x7F, 0x04, 0x08, 0x10, 0x7F, 89 | 0x3E, 0x41, 0x41, 0x41, 0x3E, 90 | 0x7F, 0x09, 0x09, 0x09, 0x06, 91 | 0x3E, 0x41, 0x51, 0x21, 0x5E, 92 | 0x7F, 0x09, 0x19, 0x29, 0x46, 93 | 0x26, 0x49, 0x49, 0x49, 0x32, 94 | 0x03, 0x01, 0x7F, 0x01, 0x03, 95 | 0x3F, 0x40, 0x40, 0x40, 0x3F, 96 | 0x1F, 0x20, 0x40, 0x20, 0x1F, 97 | 0x3F, 0x40, 0x38, 0x40, 0x3F, 98 | 0x63, 0x14, 0x08, 0x14, 0x63, 99 | 0x03, 0x04, 0x78, 0x04, 0x03, 100 | 0x61, 0x59, 0x49, 0x4D, 0x43, 101 | 0x00, 0x7F, 0x41, 0x41, 0x41, 102 | 0x02, 0x04, 0x08, 0x10, 0x20, 103 | 0x00, 0x41, 0x41, 0x41, 0x7F, 104 | 0x04, 0x02, 0x01, 0x02, 0x04, 105 | 0x40, 0x40, 0x40, 0x40, 0x40, 106 | 0x00, 0x03, 0x07, 0x08, 0x00, 107 | 0x20, 0x54, 0x54, 0x78, 0x40, 108 | 0x7F, 0x28, 0x44, 0x44, 0x38, 109 | 0x38, 0x44, 0x44, 0x44, 0x28, 110 | 0x38, 0x44, 0x44, 0x28, 0x7F, 111 | 0x38, 0x54, 0x54, 0x54, 0x18, 112 | 0x00, 0x08, 0x7E, 0x09, 0x02, 113 | 0x18, 0xA4, 0xA4, 0x9C, 0x78, 114 | 0x7F, 0x08, 0x04, 0x04, 0x78, 115 | 0x00, 0x44, 0x7D, 0x40, 0x00, 116 | 0x20, 0x40, 0x40, 0x3D, 0x00, 117 | 0x7F, 0x10, 0x28, 0x44, 0x00, 118 | 0x00, 0x41, 0x7F, 0x40, 0x00, 119 | 0x7C, 0x04, 0x78, 0x04, 0x78, 120 | 0x7C, 0x08, 0x04, 0x04, 0x78, 121 | 0x38, 0x44, 0x44, 0x44, 0x38, 122 | 0xFC, 0x18, 0x24, 0x24, 0x18, 123 | 0x18, 0x24, 0x24, 0x18, 0xFC, 124 | 0x7C, 0x08, 0x04, 0x04, 0x08, 125 | 0x48, 0x54, 0x54, 0x54, 0x24, 126 | 0x04, 0x04, 0x3F, 0x44, 0x24, 127 | 0x3C, 0x40, 0x40, 0x20, 0x7C, 128 | 0x1C, 0x20, 0x40, 0x20, 0x1C, 129 | 0x3C, 0x40, 0x30, 0x40, 0x3C, 130 | 0x44, 0x28, 0x10, 0x28, 0x44, 131 | 0x4C, 0x90, 0x90, 0x90, 0x7C, 132 | 0x44, 0x64, 0x54, 0x4C, 0x44, 133 | 0x00, 0x08, 0x36, 0x41, 0x00, 134 | 0x00, 0x00, 0x77, 0x00, 0x00, 135 | 0x00, 0x41, 0x36, 0x08, 0x00, 136 | 0x02, 0x01, 0x02, 0x04, 0x02, 137 | 0x3C, 0x26, 0x23, 0x26, 0x3C, 138 | 0x1E, 0xA1, 0xA1, 0x61, 0x12, 139 | 0x3A, 0x40, 0x40, 0x20, 0x7A, 140 | 0x38, 0x54, 0x54, 0x55, 0x59, 141 | 0x21, 0x55, 0x55, 0x79, 0x41, 142 | 0x21, 0x54, 0x54, 0x78, 0x41, 143 | 0x21, 0x55, 0x54, 0x78, 0x40, 144 | 0x20, 0x54, 0x55, 0x79, 0x40, 145 | 0x0C, 0x1E, 0x52, 0x72, 0x12, 146 | 0x39, 0x55, 0x55, 0x55, 0x59, 147 | 0x39, 0x54, 0x54, 0x54, 0x59, 148 | 0x39, 0x55, 0x54, 0x54, 0x58, 149 | 0x00, 0x00, 0x45, 0x7C, 0x41, 150 | 0x00, 0x02, 0x45, 0x7D, 0x42, 151 | 0x00, 0x01, 0x45, 0x7C, 0x40, 152 | 0xF0, 0x29, 0x24, 0x29, 0xF0, 153 | 0xF0, 0x28, 0x25, 0x28, 0xF0, 154 | 0x7C, 0x54, 0x55, 0x45, 0x00, 155 | 0x20, 0x54, 0x54, 0x7C, 0x54, 156 | 0x7C, 0x0A, 0x09, 0x7F, 0x49, 157 | 0x32, 0x49, 0x49, 0x49, 0x32, 158 | 0x32, 0x48, 0x48, 0x48, 0x32, 159 | 0x32, 0x4A, 0x48, 0x48, 0x30, 160 | 0x3A, 0x41, 0x41, 0x21, 0x7A, 161 | 0x3A, 0x42, 0x40, 0x20, 0x78, 162 | 0x00, 0x9D, 0xA0, 0xA0, 0x7D, 163 | 0x39, 0x44, 0x44, 0x44, 0x39, 164 | 0x3D, 0x40, 0x40, 0x40, 0x3D, 165 | 0x3C, 0x24, 0xFF, 0x24, 0x24, 166 | 0x48, 0x7E, 0x49, 0x43, 0x66, 167 | 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 168 | 0xFF, 0x09, 0x29, 0xF6, 0x20, 169 | 0xC0, 0x88, 0x7E, 0x09, 0x03, 170 | 0x20, 0x54, 0x54, 0x79, 0x41, 171 | 0x00, 0x00, 0x44, 0x7D, 0x41, 172 | 0x30, 0x48, 0x48, 0x4A, 0x32, 173 | 0x38, 0x40, 0x40, 0x22, 0x7A, 174 | 0x00, 0x7A, 0x0A, 0x0A, 0x72, 175 | 0x7D, 0x0D, 0x19, 0x31, 0x7D, 176 | 0x26, 0x29, 0x29, 0x2F, 0x28, 177 | 0x26, 0x29, 0x29, 0x29, 0x26, 178 | 0x30, 0x48, 0x4D, 0x40, 0x20, 179 | 0x38, 0x08, 0x08, 0x08, 0x08, 180 | 0x08, 0x08, 0x08, 0x08, 0x38, 181 | 0x2F, 0x10, 0xC8, 0xAC, 0xBA, 182 | 0x2F, 0x10, 0x28, 0x34, 0xFA, 183 | 0x00, 0x00, 0x7B, 0x00, 0x00, 184 | 0x08, 0x14, 0x2A, 0x14, 0x22, 185 | 0x22, 0x14, 0x2A, 0x14, 0x08, 186 | 0xAA, 0x00, 0x55, 0x00, 0xAA, 187 | 0xAA, 0x55, 0xAA, 0x55, 0xAA, 188 | 0x00, 0x00, 0x00, 0xFF, 0x00, 189 | 0x10, 0x10, 0x10, 0xFF, 0x00, 190 | 0x14, 0x14, 0x14, 0xFF, 0x00, 191 | 0x10, 0x10, 0xFF, 0x00, 0xFF, 192 | 0x10, 0x10, 0xF0, 0x10, 0xF0, 193 | 0x14, 0x14, 0x14, 0xFC, 0x00, 194 | 0x14, 0x14, 0xF7, 0x00, 0xFF, 195 | 0x00, 0x00, 0xFF, 0x00, 0xFF, 196 | 0x14, 0x14, 0xF4, 0x04, 0xFC, 197 | 0x14, 0x14, 0x17, 0x10, 0x1F, 198 | 0x10, 0x10, 0x1F, 0x10, 0x1F, 199 | 0x14, 0x14, 0x14, 0x1F, 0x00, 200 | 0x10, 0x10, 0x10, 0xF0, 0x00, 201 | 0x00, 0x00, 0x00, 0x1F, 0x10, 202 | 0x10, 0x10, 0x10, 0x1F, 0x10, 203 | 0x10, 0x10, 0x10, 0xF0, 0x10, 204 | 0x00, 0x00, 0x00, 0xFF, 0x10, 205 | 0x10, 0x10, 0x10, 0x10, 0x10, 206 | 0x10, 0x10, 0x10, 0xFF, 0x10, 207 | 0x00, 0x00, 0x00, 0xFF, 0x14, 208 | 0x00, 0x00, 0xFF, 0x00, 0xFF, 209 | 0x00, 0x00, 0x1F, 0x10, 0x17, 210 | 0x00, 0x00, 0xFC, 0x04, 0xF4, 211 | 0x14, 0x14, 0x17, 0x10, 0x17, 212 | 0x14, 0x14, 0xF4, 0x04, 0xF4, 213 | 0x00, 0x00, 0xFF, 0x00, 0xF7, 214 | 0x14, 0x14, 0x14, 0x14, 0x14, 215 | 0x14, 0x14, 0xF7, 0x00, 0xF7, 216 | 0x14, 0x14, 0x14, 0x17, 0x14, 217 | 0x10, 0x10, 0x1F, 0x10, 0x1F, 218 | 0x14, 0x14, 0x14, 0xF4, 0x14, 219 | 0x10, 0x10, 0xF0, 0x10, 0xF0, 220 | 0x00, 0x00, 0x1F, 0x10, 0x1F, 221 | 0x00, 0x00, 0x00, 0x1F, 0x14, 222 | 0x00, 0x00, 0x00, 0xFC, 0x14, 223 | 0x00, 0x00, 0xF0, 0x10, 0xF0, 224 | 0x10, 0x10, 0xFF, 0x10, 0xFF, 225 | 0x14, 0x14, 0x14, 0xFF, 0x14, 226 | 0x10, 0x10, 0x10, 0x1F, 0x00, 227 | 0x00, 0x00, 0x00, 0xF0, 0x10, 228 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 229 | 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 230 | 0xFF, 0xFF, 0xFF, 0x00, 0x00, 231 | 0x00, 0x00, 0x00, 0xFF, 0xFF, 232 | 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 233 | 0x38, 0x44, 0x44, 0x38, 0x44, 234 | 0x7C, 0x2A, 0x2A, 0x3E, 0x14, 235 | 0x7E, 0x02, 0x02, 0x06, 0x06, 236 | 0x02, 0x7E, 0x02, 0x7E, 0x02, 237 | 0x63, 0x55, 0x49, 0x41, 0x63, 238 | 0x38, 0x44, 0x44, 0x3C, 0x04, 239 | 0x40, 0x7E, 0x20, 0x1E, 0x20, 240 | 0x06, 0x02, 0x7E, 0x02, 0x02, 241 | 0x99, 0xA5, 0xE7, 0xA5, 0x99, 242 | 0x1C, 0x2A, 0x49, 0x2A, 0x1C, 243 | 0x4C, 0x72, 0x01, 0x72, 0x4C, 244 | 0x30, 0x4A, 0x4D, 0x4D, 0x30, 245 | 0x30, 0x48, 0x78, 0x48, 0x30, 246 | 0xBC, 0x62, 0x5A, 0x46, 0x3D, 247 | 0x3E, 0x49, 0x49, 0x49, 0x00, 248 | 0x7E, 0x01, 0x01, 0x01, 0x7E, 249 | 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 250 | 0x44, 0x44, 0x5F, 0x44, 0x44, 251 | 0x40, 0x51, 0x4A, 0x44, 0x40, 252 | 0x40, 0x44, 0x4A, 0x51, 0x40, 253 | 0x00, 0x00, 0xFF, 0x01, 0x03, 254 | 0xE0, 0x80, 0xFF, 0x00, 0x00, 255 | 0x08, 0x08, 0x6B, 0x6B, 0x08, 256 | 0x36, 0x12, 0x36, 0x24, 0x36, 257 | 0x06, 0x0F, 0x09, 0x0F, 0x06, 258 | 0x00, 0x00, 0x18, 0x18, 0x00, 259 | 0x00, 0x00, 0x10, 0x10, 0x00, 260 | 0x30, 0x40, 0xFF, 0x01, 0x01, 261 | 0x00, 0x1F, 0x01, 0x01, 0x1E, 262 | 0x00, 0x19, 0x1D, 0x17, 0x12, 263 | 0x00, 0x3C, 0x3C, 0x3C, 0x3C 264 | ])} 265 | 266 | -------------------------------------------------------------------------------- /vector3d.py: -------------------------------------------------------------------------------- 1 | # vector3d.py 3D vector class for use in inertial measurement unit drivers 2 | # Authors Peter Hinch, Sebastian Plamauer 3 | 4 | # V0.7 17th May 2017 pyb replaced with utime 5 | # V0.6 18th June 2015 6 | 7 | ''' 8 | The MIT License (MIT) 9 | Copyright (c) 2014 Sebastian Plamauer, oeplse@gmail.com, Peter Hinch 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | ''' 26 | 27 | from utime import sleep_ms 28 | from math import sqrt, degrees, acos, atan2 29 | 30 | 31 | def default_wait(): 32 | ''' 33 | delay of 50 ms 34 | ''' 35 | sleep_ms(50) 36 | 37 | 38 | class Vector3d(object): 39 | ''' 40 | Represents a vector in a 3D space using Cartesian coordinates. 41 | Internally uses sensor relative coordinates. 42 | Returns vehicle-relative x, y and z values. 43 | ''' 44 | def __init__(self, transposition, scaling, update_function): 45 | self._vector = [0, 0, 0] 46 | self._ivector = [0, 0, 0] 47 | self.cal = (0, 0, 0) 48 | self.argcheck(transposition, "Transposition") 49 | self.argcheck(scaling, "Scaling") 50 | if set(transposition) != {0, 1, 2}: 51 | raise ValueError('Transpose indices must be unique and in range 0-2') 52 | self._scale = scaling 53 | self._transpose = transposition 54 | self.update = update_function 55 | 56 | def argcheck(self, arg, name): 57 | ''' 58 | checks if arguments are of correct length 59 | ''' 60 | if len(arg) != 3 or not (type(arg) is list or type(arg) is tuple): 61 | raise ValueError(name + ' must be a 3 element list or tuple') 62 | 63 | def calibrate(self, stopfunc, waitfunc=default_wait): 64 | ''' 65 | calibration routine, sets cal 66 | ''' 67 | self.update() 68 | maxvec = self._vector[:] # Initialise max and min lists with current values 69 | minvec = self._vector[:] 70 | while not stopfunc(): 71 | waitfunc() 72 | self.update() 73 | maxvec = list(map(max, maxvec, self._vector)) 74 | minvec = list(map(min, minvec, self._vector)) 75 | self.cal = tuple(map(lambda a, b: (a + b)/2, maxvec, minvec)) 76 | 77 | @property 78 | def _calvector(self): 79 | ''' 80 | Vector adjusted for calibration offsets 81 | ''' 82 | return list(map(lambda val, offset: val - offset, self._vector, self.cal)) 83 | 84 | @property 85 | def x(self): # Corrected, vehicle relative floating point values 86 | self.update() 87 | return self._calvector[self._transpose[0]] * self._scale[0] 88 | 89 | @property 90 | def y(self): 91 | self.update() 92 | return self._calvector[self._transpose[1]] * self._scale[1] 93 | 94 | @property 95 | def z(self): 96 | self.update() 97 | return self._calvector[self._transpose[2]] * self._scale[2] 98 | 99 | @property 100 | def xyz(self): 101 | self.update() 102 | return (self._calvector[self._transpose[0]] * self._scale[0], 103 | self._calvector[self._transpose[1]] * self._scale[1], 104 | self._calvector[self._transpose[2]] * self._scale[2]) 105 | 106 | @property 107 | def magnitude(self): 108 | x, y, z = self.xyz # All measurements must correspond to the same instant 109 | return sqrt(x**2 + y**2 + z**2) 110 | 111 | @property 112 | def inclination(self): 113 | x, y, z = self.xyz 114 | return degrees(acos(z / sqrt(x**2 + y**2 + z**2))) 115 | 116 | @property 117 | def elevation(self): 118 | return 90 - self.inclination 119 | 120 | @property 121 | def azimuth(self): 122 | x, y, z = self.xyz 123 | return degrees(atan2(y, x)) 124 | 125 | # Raw uncorrected integer values from sensor 126 | @property 127 | def ix(self): 128 | return self._ivector[0] 129 | 130 | @property 131 | def iy(self): 132 | return self._ivector[1] 133 | 134 | @property 135 | def iz(self): 136 | return self._ivector[2] 137 | 138 | @property 139 | def ixyz(self): 140 | return self._ivector 141 | 142 | @property 143 | def transpose(self): 144 | return tuple(self._transpose) 145 | 146 | @property 147 | def scale(self): 148 | return tuple(self._scale) 149 | -------------------------------------------------------------------------------- /vl53l0x.py: -------------------------------------------------------------------------------- 1 | from micropython import const 2 | import ustruct 3 | import utime 4 | from machine import I2C 5 | 6 | _IO_TIMEOUT = 1000 7 | _SYSRANGE_START = const(0x00) 8 | _EXTSUP_HV = const(0x89) 9 | _MSRC_CONFIG = const(0x60) 10 | _FINAL_RATE_RTN_LIMIT = const(0x44) 11 | _SYSTEM_SEQUENCE = const(0x01) 12 | _SPAD_REF_START = const(0x4f) 13 | _SPAD_ENABLES = const(0xb0) 14 | _REF_EN_START_SELECT = const(0xb6) 15 | _SPAD_NUM_REQUESTED = const(0x4e) 16 | _INTERRUPT_GPIO = const(0x0a) 17 | _INTERRUPT_CLEAR = const(0x0b) 18 | _GPIO_MUX_ACTIVE_HIGH = const(0x84) 19 | _RESULT_INTERRUPT_STATUS = const(0x13) 20 | _RESULT_RANGE_STATUS = const(0x14) 21 | _OSC_CALIBRATE = const(0xf8) 22 | _MEASURE_PERIOD = const(0x04) 23 | 24 | class TimeoutError(RuntimeError): 25 | pass 26 | 27 | class VL53L0X: 28 | def __init__(self, i2c, address=0x29): 29 | if isinstance(i2c, str): # Non-pyb targets may use other than X or Y 30 | self.i2c = I2C(i2c) 31 | elif isinstance(i2c, int): # WiPY targets 32 | self.i2c = I2C(i2c) 33 | elif hasattr(i2c, 'readfrom'): # Soft or hard I2C instance. See issue #3097 34 | self.i2c = i2c 35 | else: 36 | raise ValueError("Invalid I2C instance") 37 | 38 | self.address = address 39 | self.init() 40 | self._started = False 41 | 42 | def _registers(self, register, values=None, struct='B'): 43 | if values is None: 44 | size = ustruct.calcsize(struct) 45 | data = self.i2c.readfrom_mem(self.address, register, size) 46 | values = ustruct.unpack(struct, data) 47 | return values 48 | data = ustruct.pack(struct, *values) 49 | self.i2c.writeto_mem(self.address, register, data) 50 | 51 | def _register(self, register, value=None, struct='B'): 52 | if value is None: 53 | return self._registers(register, struct=struct)[0] 54 | self._registers(register, (value,), struct=struct) 55 | 56 | def _flag(self, register=0x00, bit=0, value=None): 57 | data = self._register(register) 58 | mask = 1 << bit 59 | if value is None: 60 | return bool(data & mask) 61 | elif value: 62 | data |= mask 63 | else: 64 | data &= ~mask 65 | self._register(register, data) 66 | 67 | def _config(self, *config): 68 | for register, value in config: 69 | self._register(register, value) 70 | 71 | def init(self, power2v8=True): 72 | self._flag(_EXTSUP_HV, 0, power2v8) 73 | 74 | # I2C standard mode 75 | self._config( 76 | (0x88, 0x00), 77 | 78 | (0x80, 0x01), 79 | (0xff, 0x01), 80 | (0x00, 0x00), 81 | ) 82 | self._stop_variable = self._register(0x91) 83 | self._config( 84 | (0x00, 0x01), 85 | (0xff, 0x00), 86 | (0x80, 0x00), 87 | ) 88 | 89 | # disable signal_rate_msrc and signal_rate_pre_range limit checks 90 | self._flag(_MSRC_CONFIG, 1, True) 91 | self._flag(_MSRC_CONFIG, 4, True) 92 | 93 | # rate_limit = 0.25 94 | self._register(_FINAL_RATE_RTN_LIMIT, int(0.25 * (1 << 7)), 95 | struct='>H') 96 | 97 | self._register(_SYSTEM_SEQUENCE, 0xff) 98 | 99 | spad_count, is_aperture = self._spad_info() 100 | spad_map = bytearray(self._registers(_SPAD_ENABLES, struct='6B')) 101 | 102 | # set reference spads 103 | self._config( 104 | (0xff, 0x01), 105 | (_SPAD_REF_START, 0x00), 106 | (_SPAD_NUM_REQUESTED, 0x2c), 107 | (0xff, 0x00), 108 | (_REF_EN_START_SELECT, 0xb4), 109 | ) 110 | 111 | spads_enabled = 0 112 | for i in range(48): 113 | if i < 12 and is_aperture or spads_enabled >= spad_count: 114 | spad_map[i // 8] &= ~(1 << (i >> 2)) 115 | elif spad_map[i // 8] & (1 << (i >> 2)): 116 | spads_enabled += 1 117 | 118 | self._registers(_SPAD_ENABLES, spad_map, struct='6B') 119 | 120 | self._config( 121 | (0xff, 0x01), 122 | (0x00, 0x00), 123 | 124 | (0xff, 0x00), 125 | (0x09, 0x00), 126 | (0x10, 0x00), 127 | (0x11, 0x00), 128 | 129 | (0x24, 0x01), 130 | (0x25, 0xFF), 131 | (0x75, 0x00), 132 | 133 | (0xFF, 0x01), 134 | (0x4E, 0x2C), 135 | (0x48, 0x00), 136 | (0x30, 0x20), 137 | 138 | (0xFF, 0x00), 139 | (0x30, 0x09), 140 | (0x54, 0x00), 141 | (0x31, 0x04), 142 | (0x32, 0x03), 143 | (0x40, 0x83), 144 | (0x46, 0x25), 145 | (0x60, 0x00), 146 | (0x27, 0x00), 147 | (0x50, 0x06), 148 | (0x51, 0x00), 149 | (0x52, 0x96), 150 | (0x56, 0x08), 151 | (0x57, 0x30), 152 | (0x61, 0x00), 153 | (0x62, 0x00), 154 | (0x64, 0x00), 155 | (0x65, 0x00), 156 | (0x66, 0xA0), 157 | 158 | (0xFF, 0x01), 159 | (0x22, 0x32), 160 | (0x47, 0x14), 161 | (0x49, 0xFF), 162 | (0x4A, 0x00), 163 | 164 | (0xFF, 0x00), 165 | (0x7A, 0x0A), 166 | (0x7B, 0x00), 167 | (0x78, 0x21), 168 | 169 | (0xFF, 0x01), 170 | (0x23, 0x34), 171 | (0x42, 0x00), 172 | (0x44, 0xFF), 173 | (0x45, 0x26), 174 | (0x46, 0x05), 175 | (0x40, 0x40), 176 | (0x0E, 0x06), 177 | (0x20, 0x1A), 178 | (0x43, 0x40), 179 | 180 | (0xFF, 0x00), 181 | (0x34, 0x03), 182 | (0x35, 0x44), 183 | 184 | (0xFF, 0x01), 185 | (0x31, 0x04), 186 | (0x4B, 0x09), 187 | (0x4C, 0x05), 188 | (0x4D, 0x04), 189 | 190 | (0xFF, 0x00), 191 | (0x44, 0x00), 192 | (0x45, 0x20), 193 | (0x47, 0x08), 194 | (0x48, 0x28), 195 | (0x67, 0x00), 196 | (0x70, 0x04), 197 | (0x71, 0x01), 198 | (0x72, 0xFE), 199 | (0x76, 0x00), 200 | (0x77, 0x00), 201 | 202 | (0xFF, 0x01), 203 | (0x0D, 0x01), 204 | 205 | (0xFF, 0x00), 206 | (0x80, 0x01), 207 | (0x01, 0xF8), 208 | 209 | (0xFF, 0x01), 210 | (0x8E, 0x01), 211 | (0x00, 0x01), 212 | (0xFF, 0x00), 213 | (0x80, 0x00), 214 | ) 215 | 216 | self._register(_INTERRUPT_GPIO, 0x04) 217 | self._flag(_GPIO_MUX_ACTIVE_HIGH, 4, False) 218 | self._register(_INTERRUPT_CLEAR, 0x01) 219 | 220 | # XXX Need to implement this. 221 | #budget = self._timing_budget() 222 | #self._register(_SYSTEM_SEQUENCE, 0xe8) 223 | #self._timing_budget(budget) 224 | 225 | self._register(_SYSTEM_SEQUENCE, 0x01) 226 | self._calibrate(0x40) 227 | self._register(_SYSTEM_SEQUENCE, 0x02) 228 | self._calibrate(0x00) 229 | 230 | self._register(_SYSTEM_SEQUENCE, 0xe8) 231 | 232 | def _spad_info(self): 233 | self._config( 234 | (0x80, 0x01), 235 | (0xff, 0x01), 236 | (0x00, 0x00), 237 | 238 | (0xff, 0x06), 239 | ) 240 | self._flag(0x83, 3, True) 241 | self._config( 242 | (0xff, 0x07), 243 | (0x81, 0x01), 244 | 245 | (0x80, 0x01), 246 | 247 | (0x94, 0x6b), 248 | (0x83, 0x00), 249 | ) 250 | for timeout in range(_IO_TIMEOUT): 251 | if self._register(0x83): 252 | break 253 | utime.sleep_ms(1) 254 | else: 255 | raise TimeoutError() 256 | self._config( 257 | (0x83, 0x01), 258 | ) 259 | value = self._register(0x92) 260 | self._config( 261 | (0x81, 0x00), 262 | (0xff, 0x06), 263 | ) 264 | self._flag(0x83, 3, False) 265 | self._config( 266 | (0xff, 0x01), 267 | (0x00, 0x01), 268 | 269 | (0xff, 0x00), 270 | (0x80, 0x00), 271 | ) 272 | count = value & 0x7f 273 | is_aperture = bool(value & 0b10000000) 274 | return count, is_aperture 275 | 276 | def _calibrate(self, vhv_init_byte): 277 | self._register(_SYSRANGE_START, 0x01 | vhv_init_byte) 278 | for timeout in range(_IO_TIMEOUT): 279 | if self._register(_RESULT_INTERRUPT_STATUS) & 0x07: 280 | break 281 | utime.sleep_ms(1) 282 | else: 283 | raise TimeoutError() 284 | self._register(_INTERRUPT_CLEAR, 0x01) 285 | self._register(_SYSRANGE_START, 0x00) 286 | 287 | def start(self, period=0): 288 | self._config( 289 | (0x80, 0x01), 290 | (0xFF, 0x01), 291 | (0x00, 0x00), 292 | (0x91, self._stop_variable), 293 | (0x00, 0x01), 294 | (0xFF, 0x00), 295 | (0x80, 0x00), 296 | ) 297 | if period: 298 | oscilator = self._register(_OSC_CALIBRATE, struct='>H') 299 | if oscilator: 300 | period *= oscilator 301 | self._register(_MEASURE_PERIOD, period, struct='>H') 302 | self._register(_SYSRANGE_START, 0x04) 303 | else: 304 | self._register(_SYSRANGE_START, 0x02) 305 | self._started = True 306 | 307 | def stop(self): 308 | self._register(_SYSRANGE_START, 0x01) 309 | self._config( 310 | (0xFF, 0x01), 311 | (0x00, 0x00), 312 | (0x91, self._stop_variable), 313 | (0x00, 0x01), 314 | (0xFF, 0x00), 315 | ) 316 | self._started = False 317 | 318 | def read(self): 319 | if not self._started: 320 | self._config( 321 | (0x80, 0x01), 322 | (0xFF, 0x01), 323 | (0x00, 0x00), 324 | (0x91, self._stop_variable), 325 | (0x00, 0x01), 326 | (0xFF, 0x00), 327 | (0x80, 0x00), 328 | (_SYSRANGE_START, 0x01), 329 | ) 330 | for timeout in range(_IO_TIMEOUT): 331 | if not self._register(_SYSRANGE_START) & 0x01: 332 | break 333 | utime.sleep_ms(1) 334 | else: 335 | raise TimeoutError() 336 | for timeout in range(_IO_TIMEOUT): 337 | if self._register(_RESULT_INTERRUPT_STATUS) & 0x07: 338 | break 339 | utime.sleep_ms(1) 340 | else: 341 | raise TimeoutError() 342 | value = self._register(_RESULT_RANGE_STATUS + 10, struct='>H') 343 | self._register(_INTERRUPT_CLEAR, 0x01) 344 | return value 345 | --------------------------------------------------------------------------------