├── pico_person_sensor_bb.png ├── code.py ├── README.md └── LICENSE /pico_person_sensor_bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usefulsensors/person_sensor_circuit_python/HEAD/pico_person_sensor_bb.png -------------------------------------------------------------------------------- /code.py: -------------------------------------------------------------------------------- 1 | # Example of accessing the Person Sensor from Useful Sensors on a Pico using 2 | # CircuitPython. See https://usfl.ink/ps_dev for the full developer guide. 3 | 4 | import bitmaptools 5 | import board 6 | import busio 7 | import digitalio 8 | import displayio 9 | import struct 10 | import time 11 | 12 | # The person sensor has the I2C ID of hex 62, or decimal 98. 13 | PERSON_SENSOR_I2C_ADDRESS = 0x62 14 | 15 | # We will be reading raw bytes over I2C, and we'll need to decode them into 16 | # data structures. These strings define the format used for the decoding, and 17 | # are derived from the layouts defined in the developer guide. 18 | PERSON_SENSOR_I2C_HEADER_FORMAT = "BBH" 19 | PERSON_SENSOR_I2C_HEADER_BYTE_COUNT = struct.calcsize( 20 | PERSON_SENSOR_I2C_HEADER_FORMAT) 21 | 22 | PERSON_SENSOR_FACE_FORMAT = "BBBBBBbB" 23 | PERSON_SENSOR_FACE_BYTE_COUNT = struct.calcsize(PERSON_SENSOR_FACE_FORMAT) 24 | 25 | PERSON_SENSOR_FACE_MAX = 4 26 | PERSON_SENSOR_RESULT_FORMAT = PERSON_SENSOR_I2C_HEADER_FORMAT + \ 27 | "B" + PERSON_SENSOR_FACE_FORMAT * PERSON_SENSOR_FACE_MAX + "H" 28 | PERSON_SENSOR_RESULT_BYTE_COUNT = struct.calcsize(PERSON_SENSOR_RESULT_FORMAT) 29 | 30 | # How long to pause between sensor polls. 31 | PERSON_SENSOR_DELAY = 0.2 32 | 33 | # The Pico doesn't support board.I2C(), so check before calling it. If it isn't 34 | # present then we assume we're on a Pico and call an explicit function. 35 | try: 36 | i2c = board.I2C() 37 | except: 38 | i2c = busio.I2C(scl=board.GP5, sda=board.GP4) 39 | 40 | # Wait until we can access the bus. 41 | while not i2c.try_lock(): 42 | pass 43 | 44 | # For debugging purposes print out the peripheral addresses on the I2C bus. 45 | # 98 (0x62 in hex) is the address of our person sensor, and should be 46 | # present in the list. Uncomment the following three lines if you want to see 47 | # what I2C addresses are found. 48 | # while True: 49 | # print(i2c.scan()) 50 | # time.sleep(PERSON_SENSOR_DELAY) 51 | 52 | # Set up the builtin display if there's one available 53 | try: 54 | display = board.DISPLAY 55 | except: 56 | display = None 57 | if display: 58 | bitmap = displayio.Bitmap(display.width, display.height, 2) 59 | palette = displayio.Palette(2) 60 | palette[0] = 0x000000 61 | palette[1] = 0xffffff 62 | tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette) 63 | group = displayio.Group() 64 | group.append(tile_grid) 65 | display.show(group) 66 | 67 | # Keep looping and reading the person sensor results. 68 | while True: 69 | read_data = bytearray(PERSON_SENSOR_RESULT_BYTE_COUNT) 70 | i2c.readfrom_into(PERSON_SENSOR_I2C_ADDRESS, read_data) 71 | 72 | offset = 0 73 | (pad1, pad2, payload_bytes) = struct.unpack_from( 74 | PERSON_SENSOR_I2C_HEADER_FORMAT, read_data, offset) 75 | offset = offset + PERSON_SENSOR_I2C_HEADER_BYTE_COUNT 76 | 77 | (num_faces) = struct.unpack_from("B", read_data, offset) 78 | num_faces = int(num_faces[0]) 79 | offset = offset + 1 80 | 81 | faces = [] 82 | for i in range(num_faces): 83 | (box_confidence, box_left, box_top, box_right, box_bottom, id_confidence, id, 84 | is_facing) = struct.unpack_from(PERSON_SENSOR_FACE_FORMAT, read_data, offset) 85 | offset = offset + PERSON_SENSOR_FACE_BYTE_COUNT 86 | face = { 87 | "box_confidence": box_confidence, 88 | "box_left": box_left, 89 | "box_top": box_top, 90 | "box_right": box_right, 91 | "box_bottom": box_bottom, 92 | "id_confidence": id_confidence, 93 | "id": id, 94 | "is_facing": is_facing, 95 | } 96 | faces.append(face) 97 | checksum = struct.unpack_from("H", read_data, offset) 98 | print(num_faces, faces) 99 | 100 | # If the board has a display, draw rectangles for the face boxes, and a 101 | # diagonal cross if the person is facing the sensor. 102 | if display: 103 | bitmaptools.fill_region(bitmap, 0, 0, display.width, display.height, 0) 104 | for face in faces: 105 | box_left = face["box_left"] 106 | box_top = face["box_top"] 107 | box_right = face["box_right"] 108 | box_bottom = face["box_bottom"] 109 | x0 = int((box_left * display.width) / 256) 110 | y0 = int((box_top * display.height) / 256) 111 | x1 = int((box_right * display.width) / 256) 112 | y1 = int((box_bottom * display.height) / 256) 113 | print(x0, y0, x1, y1) 114 | print(display.width, display.height) 115 | bitmaptools.draw_line(bitmap, x0, y0, x1, y0, 1) 116 | bitmaptools.draw_line(bitmap, x1, y0, x1, y1, 1) 117 | bitmaptools.draw_line(bitmap, x1, y1, x0, y1, 1) 118 | bitmaptools.draw_line(bitmap, x0, y1, x0, y0, 1) 119 | if face["is_facing"]: 120 | bitmaptools.draw_line(bitmap, x0, y0, x1, y1, 1) 121 | bitmaptools.draw_line(bitmap, x1, y0, x0, y1, 1) 122 | 123 | time.sleep(PERSON_SENSOR_DELAY) 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Person Sensor in CircuitPython 2 | How to access Useful Sensor's Person Sensor from CircuitPython. 3 | 4 | ## Introduction 5 | 6 | The Person Sensor is a small hardware module that's intended to make it easy to 7 | find out when people are near a device, where they are, and who they are. It has 8 | an image sensor and a microcontroller with pretrained ML models that use 9 | computer vision to spot faces. 10 | 11 | There's a [detailed developer guide](https://usfl.ink/ps_dev) 12 | available, but this project has sample code that shows you specifically how to 13 | get the sensor up and running using CircuitPython. It has been tested with 14 | Raspberry Pi Pico and Wio Terminal boards, but should hopefully work on other 15 | CircuitPython-compatible platforms. If it isn't working on a board that isn't 16 | listed, it's likely to be a problem with either the pin numbers assigned to the 17 | I2C interface, or some other I2C initialization issue. 18 | 19 | ## Setting up Circuit Python 20 | 21 | You should read the [official guide to setting up CircuitPython on a Pico](https://learn.adafruit.com/getting-started-with-raspberry-pi-pico-circuitpython) 22 | for the latest information, but here is a summary: 23 | 24 | - Download CircuitPython for the for your board from circuitpython.org. For 25 | example the Pico version is available at https://circuitpython.org/board/raspberry_pi_pico/. 26 | This project has been tested using the `8.0.0-beta.2` version. 27 | - Hold down the `bootsel` button on the Pico and plug it into a USB port. 28 | - Drag the CircuitPython uf2 file onto the `RPI-RP2` drive that appears. 29 | 30 | Once you've followed those steps, you should see a new `CIRCUITPY` drive appear. 31 | You can now drag `code.py` files onto that drive and the Pico should run them. 32 | 33 | ## Wiring Information 34 | 35 | Wiring up the device requires 4 jumpers, to connect VDD, GND, SDA and SCL. The 36 | example here uses I2C port 0, which is assigned to GPIO4 (SDA, pin 6) and GPIO5 37 | (SCL, pin 7) in software. Power is supplied from 3V3(OUT) (pin 36), with ground 38 | attached to GND (pin 38). 39 | 40 | If you're using a device that comes with a standard connector like a Qwiic or 41 | Grove interface, you should be able to use the right cable to connect to the 42 | Qwiic connector on the sensor. You can do this on the Wio Terminal with a 43 | [Qwiic to Grove cable](https://www.sparkfun.com/products/15109) plugged into the 44 | left port (below the speaker). 45 | 46 | ### Pico Wiring 47 | 48 | For the Pico you'll need to connect directly to pins, following the wiring 49 | scheme shown below: 50 | 51 | ![Wiring diagram for Person Sensor/Pico](pico_person_sensor_bb.png) 52 | 53 | If you're using [Qwiic connectors](https://www.sparkfun.com/qwiic), the colors 54 | will be black for GND, red for 3.3V, blue for SDA, and yellow for SDC. 55 | 56 | ## Running Face Detection 57 | 58 | Copy the `code.py` file in this folder over to the `CIRCUITPY` drive. If you 59 | connect over `minicom` or a similar program to view the logs, you should see 60 | information about the faces seen by the sensor printed out, and messages about 61 | any errors encountered. If no faces are found, there should be output like this: 62 | 63 | ```bash 64 | 0 [] 65 | ``` 66 | 67 | The first number is how many face have been detected, and the second part shows 68 | information about any faces that were found. Here's an abbreviated example of 69 | the output with a face present: 70 | 71 | ``` 72 | 1 [{'box_height': 158, 'box_width': 201, 'id': -1, 'box_confidence': 99, 'box_left': ] 73 | ``` 74 | 75 | If your board has a built-in display, like the Wio Terminal, you should also see 76 | rectangles displayed on it when faces are detected. 77 | 78 | ## Troubleshooting 79 | 80 | ### Power 81 | 82 | The first thing to check is that the sensor is receiving power through the 83 | `VDD` and `GND` wires. The simplest way to test this is to hold the sensor 84 | upright (so the I2C connector is at the top) and point it at your face. You 85 | should see a green LED light up. If you don't see any response from the LED then 86 | it's likely the sensor isn't receiving power, so check those wires are set up 87 | correctly. 88 | 89 | ### Communication 90 | 91 | If you see connection errors when running the face detection example, you may 92 | have an issue with your wiring. Connection errors often look like this: 93 | 94 | ```bash 95 | Traceback (most recent call last): 96 | File "code.py", line 50, in 97 | OSError: [Errno 19] No such device 98 | ``` 99 | 100 | To help track down what's going wrong, you can uncomment the following lines in 101 | the script: 102 | 103 | ```python 104 | while True: 105 | print(i2c.scan()) 106 | time.sleep(PERSON_SENSOR_DELAY) 107 | ``` 108 | 109 | This will display which I2C devices are available in the logs. You want to make 110 | sure that address number `98` is present, because that's the one used by the 111 | person sensor. Here's an example from a board that's working: 112 | 113 | ```bash 114 | [36, 57, 79, 98, 104] 115 | ``` 116 | 117 | You can see that `98` is shown. If it isn't present then it means the sensor 118 | isn't responding to I2C messages as it should be. The most likely cause is that 119 | there's a wiring problem, so if you hit this you should double-check that the 120 | SDA and SCL wires are going to the right pins. 121 | 122 | ## Going Further 123 | 124 | This script is designed to be a simple standalone example of reading data from 125 | the person sensor, but for your own applications you may want to check out the 126 | [full library from @robotastic](https://github.com/robotastic/CircuitPython_UsefulSensors_PersonDetector). 127 | This also shows how to configure options on the sensor, and access advanced 128 | features like face recognition. -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------