├── CODE_OF_CONDUCT.md ├── Hardware ├── LED light setup │ ├── 3D printing │ │ ├── COPYRIGHT NOTICE │ │ ├── led and lens holder.STL │ │ ├── light slider.STL │ │ └── slider mounting bracket.STL │ ├── LED PCB designs │ │ ├── COPYRIGHT NOTICE │ │ ├── Design1 - Board Outline.gbr │ │ ├── Design1 - Copper Bottom.gbr │ │ ├── Design1 - Copper Top-Copper Bottom.drl │ │ ├── Design1 - Copper Top.gbr │ │ ├── Design1 - Silkscreen Bottom.gbr │ │ ├── Design1 - Silkscreen Top.gbr │ │ ├── Design1 - Solder Mask Bottom.gbr │ │ └── Design1 - Solder Mask Top.gbr │ └── laser cutter │ │ ├── COPYRIGHT NOTICE │ │ └── LED diffuser.svg ├── LICENSE ├── Original PiSpy │ ├── COPYRIGHT NOTICE │ ├── Camera extension.STL │ ├── Camera mount.stl │ ├── Laser Cut Wooden Stand.svg │ └── RPi mount.STL └── alternative designs │ ├── antbox from above │ ├── COPYRIGHT NOTICE │ ├── antbox camera mount.STL │ └── antbox wooden stand 6mm.svg │ └── lid integration with aerial camera and light │ ├── COPYRIGHT NOTICE │ └── camera and LED board mount_laser cut.svg ├── LICENSE ├── PiSpy User Manual v1.1.pdf ├── PiSpy desktop.png ├── README.md ├── Software ├── PiSpy disk image.md └── PiSpy │ ├── Day_Night_Mode.py │ ├── Image.py │ ├── Import_Trigger.py │ ├── PiSpy.py │ ├── Record.py │ └── Time_Lists.py └── okh-PiSpy.yml /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | gpask@middlebury.edu. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /Hardware/LED light setup/3D printing/COPYRIGHT NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Gregory Pask, Marcy Kittredge, and Thomas Thul 2022. 2 | 3 | This source describes Open Hardware and is licensed under the CERNOHL-S v2. 4 | 5 | You may redistribute and modify this source and make products using it 6 | under the terms of the CERN-OHL-S v2 7 | (https://ohwr.org/cern ohl s v2.txt ). 8 | 9 | This source is distributed WITHOUT ANY EXPRESS OR IMPLIED 10 | WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY 11 | QUALITY AND FITNESS FOR A PARTICULAR PURPOSE. Please see 12 | the CERN-OHL-S v2 for applicable conditions. 13 | 14 | Source location: https://github.com/gpask/PiSpy 15 | 16 | As per CERN-OHL-S v2 section 4, should You produce hardware based 17 | on this source, You must where practicable maintain the Source Location 18 | visible on the external case of the PiSpy or other products you make using 19 | this source. 20 | -------------------------------------------------------------------------------- /Hardware/LED light setup/3D printing/led and lens holder.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpask/PiSpy/4395658d6053857533d4da31a2e011c20931ea8d/Hardware/LED light setup/3D printing/led and lens holder.STL -------------------------------------------------------------------------------- /Hardware/LED light setup/3D printing/light slider.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpask/PiSpy/4395658d6053857533d4da31a2e011c20931ea8d/Hardware/LED light setup/3D printing/light slider.STL -------------------------------------------------------------------------------- /Hardware/LED light setup/3D printing/slider mounting bracket.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpask/PiSpy/4395658d6053857533d4da31a2e011c20931ea8d/Hardware/LED light setup/3D printing/slider mounting bracket.STL -------------------------------------------------------------------------------- /Hardware/LED light setup/LED PCB designs/COPYRIGHT NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Gregory Pask, Marcy Kittredge, Owen Meng, and Matt Lamparter 2022. 2 | 3 | This source describes Open Hardware and is licensed under the CERNOHL-S v2. 4 | 5 | You may redistribute and modify this source and make products using it 6 | under the terms of the CERN-OHL-S v2 7 | (https://ohwr.org/cern ohl s v2.txt ). 8 | 9 | This source is distributed WITHOUT ANY EXPRESS OR IMPLIED 10 | WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY 11 | QUALITY AND FITNESS FOR A PARTICULAR PURPOSE. Please see 12 | the CERN-OHL-S v2 for applicable conditions. 13 | 14 | Source location: https://github.com/gpask/PiSpy 15 | 16 | As per CERN-OHL-S v2 section 4, should You produce hardware based 17 | on this source, You must where practicable maintain the Source Location 18 | visible on the external case of the PiSpy or other products you make using 19 | this source. 20 | -------------------------------------------------------------------------------- /Hardware/LED light setup/LED PCB designs/Design1 - Board Outline.gbr: -------------------------------------------------------------------------------- 1 | G04 Generated by Ultiboard 14.0 * 2 | %FSLAX33Y33*% 3 | %MOMM*% 4 | 5 | %ADD10C,0.001*% 6 | %ADD11C,0.001*% 7 | 8 | 9 | G04 ColorRGB 00FFFF for the following layer * 10 | %LNBoard Outline*% 11 | %LPD*% 12 | G54D10* 13 | G54D11* 14 | X0Y0D02* 15 | X55880Y0D01* 16 | X55880Y30480D01* 17 | X0Y30480D01* 18 | X0Y0D01* 19 | 20 | M02* 21 | -------------------------------------------------------------------------------- /Hardware/LED light setup/LED PCB designs/Design1 - Copper Bottom.gbr: -------------------------------------------------------------------------------- 1 | G04 Generated by Ultiboard 14.0 * 2 | %FSLAX33Y33*% 3 | %MOMM*% 4 | 5 | %ADD10C,0.001*% 6 | %ADD11C,0.254*% 7 | %ADD12C,0.635*% 8 | %ADD13C,1.600*% 9 | 10 | 11 | G04 ColorRGB 00FF00 for the following layer * 12 | %LNCopper Bottom*% 13 | %LPD*% 14 | G54D10* 15 | G36* 16 | X55497Y30097D02* 17 | X55497Y30097D01* 18 | X383Y30097D01* 19 | X383Y383D01* 20 | X55497Y383D01* 21 | X55497Y30097D01* 22 | D02* 23 | G37* 24 | %LPC*% 25 | G36* 26 | X8129Y5080D02* 27 | G75* 28 | D01* 29 | G02X8129Y5080I2031J0* 30 | G01* 31 | D02* 32 | G37* 33 | G36* 34 | X43689Y5080D02* 35 | G75* 36 | D01* 37 | G02X43689Y5080I2031J0* 38 | G01* 39 | D02* 40 | G37* 41 | G36* 42 | X3976Y14389D02* 43 | G74* 44 | D01* 45 | G02X4661Y15074I1103J418* 46 | G01* 47 | X4661Y15074D01* 48 | X4661Y14389D01* 49 | X3976Y14389D01* 50 | D02* 51 | G37* 52 | G36* 53 | X5499Y15074D02* 54 | G74* 55 | D01* 56 | G02X6184Y14389I418J1103* 57 | G01* 58 | X6184Y14389D01* 59 | X5499Y14389D01* 60 | X5499Y15074D01* 61 | D02* 62 | G37* 63 | G36* 64 | X6184Y13551D02* 65 | G74* 66 | D01* 67 | G02X5499Y12866I1103J418* 68 | G01* 69 | X5499Y12866D01* 70 | X5499Y13551D01* 71 | X6184Y13551D01* 72 | D02* 73 | G37* 74 | G36* 75 | X4661Y12866D02* 76 | G74* 77 | D01* 78 | G02X3976Y13551I418J1103* 79 | G01* 80 | X3976Y13551D01* 81 | X4661Y13551D01* 82 | X4661Y12866D01* 83 | D02* 84 | G37* 85 | G36* 86 | X4580Y13970D02* 87 | G75* 88 | D01* 89 | G02X4580Y13970I500J0* 90 | G01* 91 | D02* 92 | G37* 93 | G36* 94 | X3899Y19050D02* 95 | G75* 96 | D01* 97 | G02X3899Y19050I1181J0* 98 | G01* 99 | D02* 100 | G37* 101 | G36* 102 | X49619Y11430D02* 103 | G75* 104 | D01* 105 | G02X49619Y11430I1181J0* 106 | G01* 107 | D02* 108 | G37* 109 | G36* 110 | X49696Y16929D02* 111 | G74* 112 | D01* 113 | G02X50381Y17614I1103J418* 114 | G01* 115 | X50381Y17614D01* 116 | X50381Y16929D01* 117 | X49696Y16929D01* 118 | D02* 119 | G37* 120 | G36* 121 | X51219Y17614D02* 122 | G74* 123 | D01* 124 | G02X51904Y16929I418J1103* 125 | G01* 126 | X51904Y16929D01* 127 | X51219Y16929D01* 128 | X51219Y17614D01* 129 | D02* 130 | G37* 131 | G36* 132 | X51904Y16091D02* 133 | G74* 134 | D01* 135 | G02X51219Y15406I1103J418* 136 | G01* 137 | X51219Y15406D01* 138 | X51219Y16091D01* 139 | X51904Y16091D01* 140 | D02* 141 | G37* 142 | G36* 143 | X50381Y15406D02* 144 | G74* 145 | D01* 146 | G02X49696Y16091I418J1103* 147 | G01* 148 | X49696Y16091D01* 149 | X50381Y16091D01* 150 | X50381Y15406D01* 151 | D02* 152 | G37* 153 | G36* 154 | X50300Y16510D02* 155 | G75* 156 | D01* 157 | G02X50300Y16510I500J0* 158 | G01* 159 | D02* 160 | G37* 161 | G36* 162 | X27167Y17006D02* 163 | X27167Y17006D01* 164 | X29501Y14672D01* 165 | X49850Y14672D01* 166 | G75* 167 | D01* 168 | G02X49850Y13268I950J-702* 169 | G01* 170 | X49850Y13268D01* 171 | X29211Y13268D01* 172 | G75* 173 | D01* 174 | G02X28714Y13474I0J702* 175 | G01* 176 | X28714Y13474D01* 177 | X26379Y15808D01* 178 | X6030Y15808D01* 179 | G75* 180 | D01* 181 | G02X6030Y17212I-950J702* 182 | G01* 183 | X6030Y17212D01* 184 | X26670Y17212D01* 185 | G74* 186 | D01* 187 | G02X27167Y17006I0J702* 188 | G01* 189 | D02* 190 | G37* 191 | G36* 192 | X50027Y22086D02* 193 | X50027Y22086D01* 194 | X51296Y20817D01* 195 | G74* 196 | D01* 197 | G02X51502Y20320I496J497* 198 | G01* 199 | X51502Y20320D01* 200 | X51502Y20000D01* 201 | G75* 202 | D01* 203 | G02X50098Y20000I-702J-950* 204 | G01* 205 | X50098Y20000D01* 206 | X50098Y20029D01* 207 | X49239Y20888D01* 208 | X4101Y20888D01* 209 | X3242Y20029D01* 210 | X3242Y12991D01* 211 | X4101Y12132D01* 212 | X4130Y12132D01* 213 | G75* 214 | D01* 215 | G02X4130Y10728I950J-702* 216 | G01* 217 | X4130Y10728D01* 218 | X3810Y10728D01* 219 | G75* 220 | D01* 221 | G02X3607Y10758I0J702* 222 | G01* 223 | G74* 224 | D01* 225 | G02X3314Y10934I204J671* 226 | G01* 227 | X3314Y10934D01* 228 | X2044Y12204D01* 229 | G75* 230 | D01* 231 | G02X1838Y12701I495J496* 232 | G01* 233 | X1838Y12701D01* 234 | X1838Y20319D01* 235 | G75* 236 | D01* 237 | G02X2044Y20816I702J0* 238 | G01* 239 | X2044Y20816D01* 240 | X3314Y22086D01* 241 | G75* 242 | D01* 243 | G02X3811Y22292I496J-495* 244 | G01* 245 | X3811Y22292D01* 246 | X49529Y22292D01* 247 | G75* 248 | D01* 249 | G02X49678Y22276I0J-702* 250 | G01* 251 | G74* 252 | D01* 253 | G02X50027Y22086I147J685* 254 | G01* 255 | D02* 256 | G37* 257 | G36* 258 | X8129Y25400D02* 259 | G75* 260 | D01* 261 | G02X8129Y25400I2031J0* 262 | G01* 263 | D02* 264 | G37* 265 | G36* 266 | X43689Y25400D02* 267 | G75* 268 | D01* 269 | G02X43689Y25400I2031J0* 270 | G01* 271 | D02* 272 | G37* 273 | %LPD*% 274 | G54D11* 275 | X8129Y5080D02* 276 | G75* 277 | D01* 278 | G02X8129Y5080I2031J0* 279 | G01* 280 | X43689Y5080D02* 281 | G75* 282 | D01* 283 | G02X43689Y5080I2031J0* 284 | G01* 285 | X3976Y14389D02* 286 | G74* 287 | D01* 288 | G02X4661Y15074I1103J418* 289 | G01* 290 | X4661Y14389D01* 291 | X3976Y14389D01* 292 | X5499Y15074D02* 293 | G74* 294 | D01* 295 | G02X6184Y14389I418J1103* 296 | G01* 297 | X5499Y14389D01* 298 | X5499Y15074D01* 299 | X6184Y13551D02* 300 | G74* 301 | D01* 302 | G02X5499Y12866I1103J418* 303 | G01* 304 | X5499Y13551D01* 305 | X6184Y13551D01* 306 | X4661Y12866D02* 307 | G74* 308 | D01* 309 | G02X3976Y13551I418J1103* 310 | G01* 311 | X4661Y13551D01* 312 | X4661Y12866D01* 313 | X4580Y13970D02* 314 | G75* 315 | D01* 316 | G02X4580Y13970I500J0* 317 | G01* 318 | X3899Y19050D02* 319 | G75* 320 | D01* 321 | G02X3899Y19050I1181J0* 322 | G01* 323 | X49619Y11430D02* 324 | G75* 325 | D01* 326 | G02X49619Y11430I1181J0* 327 | G01* 328 | X49696Y16929D02* 329 | G74* 330 | D01* 331 | G02X50381Y17614I1103J418* 332 | G01* 333 | X50381Y16929D01* 334 | X49696Y16929D01* 335 | X51219Y17614D02* 336 | G74* 337 | D01* 338 | G02X51904Y16929I418J1103* 339 | G01* 340 | X51219Y16929D01* 341 | X51219Y17614D01* 342 | X51904Y16091D02* 343 | G74* 344 | D01* 345 | G02X51219Y15406I1103J418* 346 | G01* 347 | X51219Y16091D01* 348 | X51904Y16091D01* 349 | X50381Y15406D02* 350 | G74* 351 | D01* 352 | G02X49696Y16091I418J1103* 353 | G01* 354 | X50381Y16091D01* 355 | X50381Y15406D01* 356 | X50300Y16510D02* 357 | G75* 358 | D01* 359 | G02X50300Y16510I500J0* 360 | G01* 361 | X27167Y17006D02* 362 | X29501Y14672D01* 363 | X49850Y14672D01* 364 | G75* 365 | D01* 366 | G02X49850Y13268I950J-702* 367 | G01* 368 | X29211Y13268D01* 369 | G75* 370 | D01* 371 | G02X28714Y13474I0J702* 372 | G01* 373 | X26379Y15808D01* 374 | X6030Y15808D01* 375 | G75* 376 | D01* 377 | G02X6030Y17212I-950J702* 378 | G01* 379 | X26670Y17212D01* 380 | G74* 381 | D01* 382 | G02X27167Y17006I0J702* 383 | G01* 384 | X50027Y22086D02* 385 | X51296Y20817D01* 386 | G74* 387 | D01* 388 | G02X51502Y20320I496J497* 389 | G01* 390 | X51502Y20000D01* 391 | G75* 392 | D01* 393 | G02X50098Y20000I-702J-950* 394 | G01* 395 | X50098Y20029D01* 396 | X49239Y20888D01* 397 | X4101Y20888D01* 398 | X3242Y20029D01* 399 | X3242Y12991D01* 400 | X4101Y12132D01* 401 | X4130Y12132D01* 402 | G75* 403 | D01* 404 | G02X4130Y10728I950J-702* 405 | G01* 406 | X3810Y10728D01* 407 | G75* 408 | D01* 409 | G02X3607Y10758I0J702* 410 | G01* 411 | G74* 412 | D01* 413 | G02X3314Y10934I204J671* 414 | G01* 415 | X2044Y12204D01* 416 | G75* 417 | D01* 418 | G02X1838Y12701I495J496* 419 | G01* 420 | X1838Y20319D01* 421 | G75* 422 | D01* 423 | G02X2044Y20816I702J0* 424 | G01* 425 | X3314Y22086D01* 426 | G75* 427 | D01* 428 | G02X3811Y22292I496J-495* 429 | G01* 430 | X49529Y22292D01* 431 | G75* 432 | D01* 433 | G02X49678Y22276I0J-702* 434 | G01* 435 | G74* 436 | D01* 437 | G02X50027Y22086I147J685* 438 | G01* 439 | X8129Y25400D02* 440 | G75* 441 | D01* 442 | G02X8129Y25400I2031J0* 443 | G01* 444 | X43689Y25400D02* 445 | G75* 446 | D01* 447 | G02X43689Y25400I2031J0* 448 | G01* 449 | X55497Y30097D02* 450 | X383Y30097D01* 451 | X383Y383D01* 452 | X55497Y383D01* 453 | X55497Y30097D01* 454 | G54D12* 455 | X5080Y16510D02* 456 | X26670Y16510D01* 457 | X29210Y13970D01* 458 | X50800Y13970D01* 459 | X50800Y19050D02* 460 | X50800Y20320D01* 461 | X49530Y21590D01* 462 | X5080Y11430D02* 463 | X3810Y11430D01* 464 | X3810Y21590D02* 465 | X49530Y21590D01* 466 | X2540Y12700D02* 467 | X2540Y20320D01* 468 | X3810Y11430D02* 469 | X2540Y12700D01* 470 | X2540Y20320D02* 471 | X3810Y21590D01* 472 | G54D13* 473 | X5080Y19050D03* 474 | X5080Y13970D03* 475 | X5080Y16510D03* 476 | X5080Y11430D03* 477 | X50800Y16510D03* 478 | X50800Y11430D03* 479 | X50800Y19050D03* 480 | X50800Y13970D03* 481 | 482 | M02* 483 | -------------------------------------------------------------------------------- /Hardware/LED light setup/LED PCB designs/Design1 - Copper Top-Copper Bottom.drl: -------------------------------------------------------------------------------- 1 | M48 2 | METRIC,TZ 3 | VER,1 4 | FMAT,2 5 | DETECT,ON 6 | ATC,ON 7 | T1C3.300 8 | T2C1.000 9 | % 10 | T1 11 | X10160Y25400 12 | Y5080 13 | X45720Y25400 14 | Y5080 15 | T2 16 | X5080Y19050 17 | Y13970 18 | Y16510 19 | Y11430 20 | X50800Y16510 21 | Y11430 22 | Y19050 23 | Y13970 24 | M30 25 | -------------------------------------------------------------------------------- /Hardware/LED light setup/LED PCB designs/Design1 - Silkscreen Bottom.gbr: -------------------------------------------------------------------------------- 1 | G04 Generated by Ultiboard 14.0 * 2 | %FSLAX33Y33*% 3 | %MOMM*% 4 | 5 | %ADD10C,0.001*% 6 | %ADD11C,0.156*% 7 | %ADD12C,0.200*% 8 | 9 | 10 | G04 ColorRGB FF14FF for the following layer * 11 | %LNSilkscreen Bottom*% 12 | %LPD*% 13 | G54D10* 14 | G54D11* 15 | X4880Y21429D02* 16 | X4680Y21628D01* 17 | X4680Y21827D01* 18 | X4880Y22026D01* 19 | X5680Y22026D01* 20 | X5480Y22424D02* 21 | X5680Y22623D01* 22 | X4680Y22623D01* 23 | X4680Y22325D02* 24 | X4680Y22922D01* 25 | X50600Y21429D02* 26 | X50400Y21628D01* 27 | X50400Y21827D01* 28 | X50600Y22026D01* 29 | X51400Y22026D01* 30 | X51200Y22325D02* 31 | X51400Y22524D01* 32 | X51400Y22723D01* 33 | X51200Y22922D01* 34 | X51100Y22922D01* 35 | X50400Y22325D01* 36 | X50400Y22922D01* 37 | X50500Y22922D01* 38 | X8925Y19249D02* 39 | X8925Y19846D01* 40 | X9325Y19846D01* 41 | X9325Y19448D01* 42 | X9525Y19249D01* 43 | X9725Y19249D01* 44 | X9925Y19448D01* 45 | X9925Y19846D01* 46 | X8925Y18950D02* 47 | X9925Y18652D01* 48 | X8925Y18353D01* 49 | X9925Y17306D02* 50 | X8925Y17306D01* 51 | X8925Y16908D01* 52 | X9125Y16709D01* 53 | X9225Y16709D01* 54 | X9425Y16908D01* 55 | X9425Y17306D01* 56 | X9425Y17207D02* 57 | X9925Y16709D01* 58 | X9425Y13920D02* 59 | X9425Y13721D01* 60 | X9725Y13721D01* 61 | X9925Y13920D01* 62 | X9925Y14119D01* 63 | X9725Y14318D01* 64 | X9125Y14318D01* 65 | X8925Y14119D01* 66 | X8925Y13721D01* 67 | X8925Y11778D02* 68 | X9925Y11679D01* 69 | X9525Y11480D01* 70 | X9925Y11281D01* 71 | X8925Y11181D01* 72 | X46320Y11231D02* 73 | X46320Y10634D01* 74 | X45920Y10634D01* 75 | X45920Y11032D01* 76 | X45720Y11231D01* 77 | X45520Y11231D01* 78 | X45320Y11032D01* 79 | X45320Y10634D01* 80 | X46320Y11530D02* 81 | X45320Y11828D01* 82 | X46320Y12127D01* 83 | X45320Y13174D02* 84 | X46320Y13174D01* 85 | X46320Y13572D01* 86 | X46120Y13771D01* 87 | X46020Y13771D01* 88 | X45820Y13572D01* 89 | X45820Y13174D01* 90 | X45820Y13273D02* 91 | X45320Y13771D01* 92 | X45820Y16560D02* 93 | X45820Y16759D01* 94 | X45520Y16759D01* 95 | X45320Y16560D01* 96 | X45320Y16361D01* 97 | X45520Y16162D01* 98 | X46120Y16162D01* 99 | X46320Y16361D01* 100 | X46320Y16759D01* 101 | X46320Y18702D02* 102 | X45320Y18801D01* 103 | X45720Y19000D01* 104 | X45320Y19199D01* 105 | X46320Y19299D01* 106 | G54D12* 107 | X1680Y20675D02* 108 | X8480Y20675D01* 109 | X8480Y9805D01* 110 | X1680Y9805D01* 111 | X1680Y20675D01* 112 | X54200Y9805D02* 113 | X47400Y9805D01* 114 | X47400Y20675D01* 115 | X54200Y20675D01* 116 | X54200Y9805D01* 117 | 118 | M02* 119 | -------------------------------------------------------------------------------- /Hardware/LED light setup/LED PCB designs/Design1 - Silkscreen Top.gbr: -------------------------------------------------------------------------------- 1 | G04 Generated by Ultiboard 14.0 * 2 | %FSLAX33Y33*% 3 | %MOMM*% 4 | 5 | %ADD10C,0.001*% 6 | %ADD11C,0.156*% 7 | %ADD12C,0.203*% 8 | %ADD13C,0.305*% 9 | %ADD14C,0.200*% 10 | %ADD15C,0.250*% 11 | 12 | 13 | G04 ColorRGB FFFF00 for the following layer * 14 | %LNSilkscreen Top*% 15 | %LPD*% 16 | G54D10* 17 | G54D11* 18 | X19085Y16462D02* 19 | X20085Y16561D01* 20 | X19685Y16760D01* 21 | X20085Y16959D01* 22 | X19085Y17059D01* 23 | X20085Y17358D02* 24 | X19085Y17358D01* 25 | X20085Y17955D02* 26 | X19085Y17955D01* 27 | X19585Y17358D02* 28 | X19585Y17955D01* 29 | X20085Y18453D02* 30 | X20085Y18652D01* 31 | X19085Y18453D02* 32 | X19085Y18652D01* 33 | X20085Y18552D02* 34 | X19085Y18552D01* 35 | X20085Y19448D02* 36 | X19085Y19448D01* 37 | X19085Y19150D02* 38 | X19085Y19747D01* 39 | X20085Y20643D02* 40 | X20085Y20046D01* 41 | X19585Y20046D01* 42 | X19085Y20046D01* 43 | X19085Y20643D01* 44 | X19585Y20046D02* 45 | X19585Y20444D01* 46 | X19285Y21041D02* 47 | X19085Y21240D01* 48 | X20085Y21240D01* 49 | X20085Y20942D02* 50 | X20085Y21539D01* 51 | X24165Y16462D02* 52 | X25165Y16561D01* 53 | X24765Y16760D01* 54 | X25165Y16959D01* 55 | X24165Y17059D01* 56 | X25165Y17358D02* 57 | X24165Y17358D01* 58 | X25165Y17955D02* 59 | X24165Y17955D01* 60 | X24665Y17358D02* 61 | X24665Y17955D01* 62 | X25165Y18453D02* 63 | X25165Y18652D01* 64 | X24165Y18453D02* 65 | X24165Y18652D01* 66 | X25165Y18552D02* 67 | X24165Y18552D01* 68 | X25165Y19448D02* 69 | X24165Y19448D01* 70 | X24165Y19150D02* 71 | X24165Y19747D01* 72 | X25165Y20643D02* 73 | X25165Y20046D01* 74 | X24665Y20046D01* 75 | X24165Y20046D01* 76 | X24165Y20643D01* 77 | X24665Y20046D02* 78 | X24665Y20444D01* 79 | X24365Y20942D02* 80 | X24165Y21141D01* 81 | X24165Y21340D01* 82 | X24365Y21539D01* 83 | X24465Y21539D01* 84 | X25165Y20942D01* 85 | X25165Y21539D01* 86 | X25065Y21539D01* 87 | X29245Y16462D02* 88 | X30245Y16561D01* 89 | X29845Y16760D01* 90 | X30245Y16959D01* 91 | X29245Y17059D01* 92 | X30245Y17358D02* 93 | X29245Y17358D01* 94 | X30245Y17955D02* 95 | X29245Y17955D01* 96 | X29745Y17358D02* 97 | X29745Y17955D01* 98 | X30245Y18453D02* 99 | X30245Y18652D01* 100 | X29245Y18453D02* 101 | X29245Y18652D01* 102 | X30245Y18552D02* 103 | X29245Y18552D01* 104 | X30245Y19448D02* 105 | X29245Y19448D01* 106 | X29245Y19150D02* 107 | X29245Y19747D01* 108 | X30245Y20643D02* 109 | X30245Y20046D01* 110 | X29745Y20046D01* 111 | X29245Y20046D01* 112 | X29245Y20643D01* 113 | X29745Y20046D02* 114 | X29745Y20444D01* 115 | X29345Y21041D02* 116 | X29245Y21141D01* 117 | X29245Y21340D01* 118 | X29445Y21539D01* 119 | X29645Y21539D01* 120 | X29745Y21439D01* 121 | X29845Y21539D01* 122 | X30045Y21539D01* 123 | X30245Y21340D01* 124 | X30245Y21141D01* 125 | X30145Y21041D01* 126 | X29745Y21141D02* 127 | X29745Y21439D01* 128 | X34325Y16462D02* 129 | X35325Y16561D01* 130 | X34925Y16760D01* 131 | X35325Y16959D01* 132 | X34325Y17059D01* 133 | X35325Y17358D02* 134 | X34325Y17358D01* 135 | X35325Y17955D02* 136 | X34325Y17955D01* 137 | X34825Y17358D02* 138 | X34825Y17955D01* 139 | X35325Y18453D02* 140 | X35325Y18652D01* 141 | X34325Y18453D02* 142 | X34325Y18652D01* 143 | X35325Y18552D02* 144 | X34325Y18552D01* 145 | X35325Y19448D02* 146 | X34325Y19448D01* 147 | X34325Y19150D02* 148 | X34325Y19747D01* 149 | X35325Y20643D02* 150 | X35325Y20046D01* 151 | X34825Y20046D01* 152 | X34325Y20046D01* 153 | X34325Y20643D01* 154 | X34825Y20046D02* 155 | X34825Y20444D01* 156 | X34925Y21539D02* 157 | X34925Y20942D01* 158 | X34325Y21439D01* 159 | X35325Y21439D01* 160 | X35325Y21340D02* 161 | X35325Y21539D01* 162 | X39405Y16462D02* 163 | X40405Y16561D01* 164 | X40005Y16760D01* 165 | X40405Y16959D01* 166 | X39405Y17059D01* 167 | X40405Y17358D02* 168 | X39405Y17358D01* 169 | X40405Y17955D02* 170 | X39405Y17955D01* 171 | X39905Y17358D02* 172 | X39905Y17955D01* 173 | X40405Y18453D02* 174 | X40405Y18652D01* 175 | X39405Y18453D02* 176 | X39405Y18652D01* 177 | X40405Y18552D02* 178 | X39405Y18552D01* 179 | X40405Y19448D02* 180 | X39405Y19448D01* 181 | X39405Y19150D02* 182 | X39405Y19747D01* 183 | X40405Y20643D02* 184 | X40405Y20046D01* 185 | X39905Y20046D01* 186 | X39405Y20046D01* 187 | X39405Y20643D01* 188 | X39905Y20046D02* 189 | X39905Y20444D01* 190 | X39405Y21539D02* 191 | X39405Y20942D01* 192 | X39805Y20942D01* 193 | X39805Y21340D01* 194 | X40005Y21539D01* 195 | X40205Y21539D01* 196 | X40405Y21340D01* 197 | X40405Y20942D01* 198 | X20150Y24604D02* 199 | X19150Y24604D01* 200 | X19150Y25002D01* 201 | X19350Y25201D01* 202 | X19450Y25201D01* 203 | X19650Y25002D01* 204 | X19650Y24604D01* 205 | X19650Y24703D02* 206 | X20150Y25201D01* 207 | X20150Y25898D02* 208 | X20150Y25699D01* 209 | X19950Y25500D01* 210 | X19750Y25500D01* 211 | X19650Y25599D01* 212 | X19550Y25500D01* 213 | X19350Y25500D01* 214 | X19150Y25699D01* 215 | X19150Y25898D01* 216 | X19350Y26097D01* 217 | X19550Y26097D01* 218 | X19650Y25997D01* 219 | X19750Y26097D01* 220 | X19950Y26097D01* 221 | X20150Y25898D01* 222 | X19650Y25599D02* 223 | X19650Y25997D01* 224 | X25230Y24604D02* 225 | X24230Y24604D01* 226 | X24230Y25002D01* 227 | X24430Y25201D01* 228 | X24530Y25201D01* 229 | X24730Y25002D01* 230 | X24730Y24604D01* 231 | X24730Y24703D02* 232 | X25230Y25201D01* 233 | X25030Y25500D02* 234 | X25230Y25699D01* 235 | X25230Y25898D01* 236 | X25030Y26097D01* 237 | X24630Y26097D01* 238 | X24430Y26097D01* 239 | X24230Y25898D01* 240 | X24230Y25699D01* 241 | X24430Y25500D01* 242 | X24630Y25500D01* 243 | X24830Y25699D01* 244 | X24830Y25898D01* 245 | X24630Y26097D01* 246 | X30310Y24156D02* 247 | X29310Y24156D01* 248 | X29310Y24554D01* 249 | X29510Y24753D01* 250 | X29610Y24753D01* 251 | X29810Y24554D01* 252 | X29810Y24156D01* 253 | X29810Y24255D02* 254 | X30310Y24753D01* 255 | X29510Y25151D02* 256 | X29310Y25350D01* 257 | X30310Y25350D01* 258 | X30310Y25052D02* 259 | X30310Y25649D01* 260 | X29510Y25948D02* 261 | X29310Y26147D01* 262 | X29310Y26346D01* 263 | X29510Y26545D01* 264 | X30110Y26545D01* 265 | X30310Y26346D01* 266 | X30310Y26147D01* 267 | X30110Y25948D01* 268 | X29510Y25948D01* 269 | X29510Y26545D02* 270 | X30110Y25948D01* 271 | X35390Y24156D02* 272 | X34390Y24156D01* 273 | X34390Y24554D01* 274 | X34590Y24753D01* 275 | X34690Y24753D01* 276 | X34890Y24554D01* 277 | X34890Y24156D01* 278 | X34890Y24255D02* 279 | X35390Y24753D01* 280 | X34590Y25151D02* 281 | X34390Y25350D01* 282 | X35390Y25350D01* 283 | X35390Y25052D02* 284 | X35390Y25649D01* 285 | X34590Y26047D02* 286 | X34390Y26246D01* 287 | X35390Y26246D01* 288 | X35390Y25948D02* 289 | X35390Y26545D01* 290 | X40470Y24156D02* 291 | X39470Y24156D01* 292 | X39470Y24554D01* 293 | X39670Y24753D01* 294 | X39770Y24753D01* 295 | X39970Y24554D01* 296 | X39970Y24156D01* 297 | X39970Y24255D02* 298 | X40470Y24753D01* 299 | X39670Y25151D02* 300 | X39470Y25350D01* 301 | X40470Y25350D01* 302 | X40470Y25052D02* 303 | X40470Y25649D01* 304 | X39670Y25948D02* 305 | X39470Y26147D01* 306 | X39470Y26346D01* 307 | X39670Y26545D01* 308 | X39770Y26545D01* 309 | X40470Y25948D01* 310 | X40470Y26545D01* 311 | X40370Y26545D01* 312 | X20085Y9738D02* 313 | X19085Y9738D01* 314 | X19085Y10136D01* 315 | X19285Y10335D01* 316 | X19385Y10335D01* 317 | X19585Y10136D01* 318 | X19585Y9738D01* 319 | X19585Y9837D02* 320 | X20085Y10335D01* 321 | X20085Y11231D02* 322 | X20085Y10634D01* 323 | X19585Y10634D01* 324 | X19085Y10634D01* 325 | X19085Y11231D01* 326 | X19585Y10634D02* 327 | X19585Y11032D01* 328 | X20085Y11530D02* 329 | X20085Y11928D01* 330 | X19885Y12127D01* 331 | X19285Y12127D01* 332 | X19085Y11928D01* 333 | X19085Y11530D01* 334 | X19085Y11629D02* 335 | X20085Y11629D01* 336 | X19085Y13023D02* 337 | X19085Y12426D01* 338 | X19485Y12426D01* 339 | X19485Y12824D01* 340 | X19685Y13023D01* 341 | X19885Y13023D01* 342 | X20085Y12824D01* 343 | X20085Y12426D01* 344 | X25165Y9738D02* 345 | X24165Y9738D01* 346 | X24165Y10136D01* 347 | X24365Y10335D01* 348 | X24465Y10335D01* 349 | X24665Y10136D01* 350 | X24665Y9738D01* 351 | X24665Y9837D02* 352 | X25165Y10335D01* 353 | X25165Y11231D02* 354 | X25165Y10634D01* 355 | X24665Y10634D01* 356 | X24165Y10634D01* 357 | X24165Y11231D01* 358 | X24665Y10634D02* 359 | X24665Y11032D01* 360 | X25165Y11530D02* 361 | X25165Y11928D01* 362 | X24965Y12127D01* 363 | X24365Y12127D01* 364 | X24165Y11928D01* 365 | X24165Y11530D01* 366 | X24165Y11629D02* 367 | X25165Y11629D01* 368 | X24765Y13023D02* 369 | X24765Y12426D01* 370 | X24165Y12923D01* 371 | X25165Y12923D01* 372 | X25165Y12824D02* 373 | X25165Y13023D01* 374 | X30245Y9738D02* 375 | X29245Y9738D01* 376 | X29245Y10136D01* 377 | X29445Y10335D01* 378 | X29545Y10335D01* 379 | X29745Y10136D01* 380 | X29745Y9738D01* 381 | X29745Y9837D02* 382 | X30245Y10335D01* 383 | X30245Y11231D02* 384 | X30245Y10634D01* 385 | X29745Y10634D01* 386 | X29245Y10634D01* 387 | X29245Y11231D01* 388 | X29745Y10634D02* 389 | X29745Y11032D01* 390 | X30245Y11530D02* 391 | X30245Y11928D01* 392 | X30045Y12127D01* 393 | X29445Y12127D01* 394 | X29245Y11928D01* 395 | X29245Y11530D01* 396 | X29245Y11629D02* 397 | X30245Y11629D01* 398 | X29345Y12525D02* 399 | X29245Y12625D01* 400 | X29245Y12824D01* 401 | X29445Y13023D01* 402 | X29645Y13023D01* 403 | X29745Y12923D01* 404 | X29845Y13023D01* 405 | X30045Y13023D01* 406 | X30245Y12824D01* 407 | X30245Y12625D01* 408 | X30145Y12525D01* 409 | X29745Y12625D02* 410 | X29745Y12923D01* 411 | X35325Y9738D02* 412 | X34325Y9738D01* 413 | X34325Y10136D01* 414 | X34525Y10335D01* 415 | X34625Y10335D01* 416 | X34825Y10136D01* 417 | X34825Y9738D01* 418 | X34825Y9837D02* 419 | X35325Y10335D01* 420 | X35325Y11231D02* 421 | X35325Y10634D01* 422 | X34825Y10634D01* 423 | X34325Y10634D01* 424 | X34325Y11231D01* 425 | X34825Y10634D02* 426 | X34825Y11032D01* 427 | X35325Y11530D02* 428 | X35325Y11928D01* 429 | X35125Y12127D01* 430 | X34525Y12127D01* 431 | X34325Y11928D01* 432 | X34325Y11530D01* 433 | X34325Y11629D02* 434 | X35325Y11629D01* 435 | X34525Y12426D02* 436 | X34325Y12625D01* 437 | X34325Y12824D01* 438 | X34525Y13023D01* 439 | X34625Y13023D01* 440 | X35325Y12426D01* 441 | X35325Y13023D01* 442 | X35225Y13023D01* 443 | X40405Y9738D02* 444 | X39405Y9738D01* 445 | X39405Y10136D01* 446 | X39605Y10335D01* 447 | X39705Y10335D01* 448 | X39905Y10136D01* 449 | X39905Y9738D01* 450 | X39905Y9837D02* 451 | X40405Y10335D01* 452 | X40405Y11231D02* 453 | X40405Y10634D01* 454 | X39905Y10634D01* 455 | X39405Y10634D01* 456 | X39405Y11231D01* 457 | X39905Y10634D02* 458 | X39905Y11032D01* 459 | X40405Y11530D02* 460 | X40405Y11928D01* 461 | X40205Y12127D01* 462 | X39605Y12127D01* 463 | X39405Y11928D01* 464 | X39405Y11530D01* 465 | X39405Y11629D02* 466 | X40405Y11629D01* 467 | X39605Y12525D02* 468 | X39405Y12724D01* 469 | X40405Y12724D01* 470 | X40405Y12426D02* 471 | X40405Y13023D01* 472 | X20150Y4284D02* 473 | X19150Y4284D01* 474 | X19150Y4682D01* 475 | X19350Y4881D01* 476 | X19450Y4881D01* 477 | X19650Y4682D01* 478 | X19650Y4284D01* 479 | X19650Y4383D02* 480 | X20150Y4881D01* 481 | X19150Y5677D02* 482 | X19150Y5379D01* 483 | X19350Y5180D01* 484 | X19750Y5180D01* 485 | X19950Y5180D01* 486 | X20150Y5379D01* 487 | X20150Y5578D01* 488 | X19950Y5777D01* 489 | X19750Y5777D01* 490 | X19550Y5578D01* 491 | X19550Y5379D01* 492 | X19750Y5180D01* 493 | X25230Y4284D02* 494 | X24230Y4284D01* 495 | X24230Y4682D01* 496 | X24430Y4881D01* 497 | X24530Y4881D01* 498 | X24730Y4682D01* 499 | X24730Y4284D01* 500 | X24730Y4383D02* 501 | X25230Y4881D01* 502 | X24230Y5777D02* 503 | X24230Y5180D01* 504 | X24630Y5180D01* 505 | X24630Y5578D01* 506 | X24830Y5777D01* 507 | X25030Y5777D01* 508 | X25230Y5578D01* 509 | X25230Y5180D01* 510 | X30310Y4284D02* 511 | X29310Y4284D01* 512 | X29310Y4682D01* 513 | X29510Y4881D01* 514 | X29610Y4881D01* 515 | X29810Y4682D01* 516 | X29810Y4284D01* 517 | X29810Y4383D02* 518 | X30310Y4881D01* 519 | X29910Y5777D02* 520 | X29910Y5180D01* 521 | X29310Y5677D01* 522 | X30310Y5677D01* 523 | X30310Y5578D02* 524 | X30310Y5777D01* 525 | X35390Y4284D02* 526 | X34390Y4284D01* 527 | X34390Y4682D01* 528 | X34590Y4881D01* 529 | X34690Y4881D01* 530 | X34890Y4682D01* 531 | X34890Y4284D01* 532 | X34890Y4383D02* 533 | X35390Y4881D01* 534 | X34490Y5279D02* 535 | X34390Y5379D01* 536 | X34390Y5578D01* 537 | X34590Y5777D01* 538 | X34790Y5777D01* 539 | X34890Y5677D01* 540 | X34990Y5777D01* 541 | X35190Y5777D01* 542 | X35390Y5578D01* 543 | X35390Y5379D01* 544 | X35290Y5279D01* 545 | X34890Y5379D02* 546 | X34890Y5677D01* 547 | X40470Y4284D02* 548 | X39470Y4284D01* 549 | X39470Y4682D01* 550 | X39670Y4881D01* 551 | X39770Y4881D01* 552 | X39970Y4682D01* 553 | X39970Y4284D01* 554 | X39970Y4383D02* 555 | X40470Y4881D01* 556 | X39670Y5180D02* 557 | X39470Y5379D01* 558 | X39470Y5578D01* 559 | X39670Y5777D01* 560 | X39770Y5777D01* 561 | X40470Y5180D01* 562 | X40470Y5777D01* 563 | X40370Y5777D01* 564 | X13600Y20159D02* 565 | X13800Y20358D01* 566 | X13800Y20557D01* 567 | X13600Y20756D01* 568 | X13000Y20756D01* 569 | X12800Y20557D01* 570 | X12800Y20358D01* 571 | X13000Y20159D01* 572 | X13600Y20159D01* 573 | X13600Y20557D02* 574 | X13800Y20756D01* 575 | X13000Y21055D02* 576 | X12800Y21254D01* 577 | X12800Y21453D01* 578 | X13000Y21652D01* 579 | X13100Y21652D01* 580 | X13800Y21055D01* 581 | X13800Y21652D01* 582 | X13700Y21652D01* 583 | X13800Y8094D02* 584 | X12800Y8094D01* 585 | X12800Y8492D01* 586 | X13000Y8691D01* 587 | X13100Y8691D01* 588 | X13300Y8492D01* 589 | X13300Y8094D01* 590 | X13300Y8193D02* 591 | X13800Y8691D01* 592 | X13800Y9288D02* 593 | X13300Y9288D01* 594 | X13000Y9587D01* 595 | X12800Y9587D01* 596 | X12800Y8990D01* 597 | X13000Y8990D01* 598 | X44280Y20794D02* 599 | X43280Y20794D01* 600 | X43280Y21192D01* 601 | X43480Y21391D01* 602 | X43580Y21391D01* 603 | X43780Y21192D01* 604 | X43780Y20794D01* 605 | X43780Y20893D02* 606 | X44280Y21391D01* 607 | X43480Y21789D02* 608 | X43280Y21988D01* 609 | X44280Y21988D01* 610 | X44280Y21690D02* 611 | X44280Y22287D01* 612 | X44080Y8729D02* 613 | X44280Y8928D01* 614 | X44280Y9127D01* 615 | X44080Y9326D01* 616 | X43480Y9326D01* 617 | X43280Y9127D01* 618 | X43280Y8928D01* 619 | X43480Y8729D01* 620 | X44080Y8729D01* 621 | X44080Y9127D02* 622 | X44280Y9326D01* 623 | X43480Y9724D02* 624 | X43280Y9923D01* 625 | X44280Y9923D01* 626 | X44280Y9625D02* 627 | X44280Y10222D01* 628 | X7020Y11082D02* 629 | X8020Y11181D01* 630 | X7620Y11380D01* 631 | X8020Y11579D01* 632 | X7020Y11679D01* 633 | X7520Y14020D02* 634 | X7520Y14219D01* 635 | X7820Y14219D01* 636 | X8020Y14020D01* 637 | X8020Y13821D01* 638 | X7820Y13622D01* 639 | X7220Y13622D01* 640 | X7020Y13821D01* 641 | X7020Y14219D01* 642 | X8020Y15714D02* 643 | X7020Y15714D01* 644 | X7020Y16112D01* 645 | X7220Y16311D01* 646 | X7320Y16311D01* 647 | X7520Y16112D01* 648 | X7520Y15714D01* 649 | X7520Y15813D02* 650 | X8020Y16311D01* 651 | X7020Y18851D02* 652 | X7020Y18254D01* 653 | X7420Y18254D01* 654 | X7420Y18652D01* 655 | X7620Y18851D01* 656 | X7820Y18851D01* 657 | X8020Y18652D01* 658 | X8020Y18254D01* 659 | X7020Y19150D02* 660 | X8020Y19448D01* 661 | X7020Y19747D01* 662 | X47660Y11231D02* 663 | X47660Y10634D01* 664 | X48060Y10634D01* 665 | X48060Y11032D01* 666 | X48260Y11231D01* 667 | X48460Y11231D01* 668 | X48660Y11032D01* 669 | X48660Y10634D01* 670 | X47660Y11530D02* 671 | X48660Y11828D01* 672 | X47660Y12127D01* 673 | X48660Y13174D02* 674 | X47660Y13174D01* 675 | X47660Y13572D01* 676 | X47860Y13771D01* 677 | X47960Y13771D01* 678 | X48160Y13572D01* 679 | X48160Y13174D01* 680 | X48160Y13273D02* 681 | X48660Y13771D01* 682 | X48160Y16560D02* 683 | X48160Y16759D01* 684 | X48460Y16759D01* 685 | X48660Y16560D01* 686 | X48660Y16361D01* 687 | X48460Y16162D01* 688 | X47860Y16162D01* 689 | X47660Y16361D01* 690 | X47660Y16759D01* 691 | X47660Y18702D02* 692 | X48660Y18801D01* 693 | X48260Y19000D01* 694 | X48660Y19199D01* 695 | X47660Y19299D01* 696 | G54D12* 697 | X18780Y21550D02* 698 | X16780Y21550D01* 699 | X16780Y16550D02* 700 | X18780Y16550D01* 701 | X16780Y18300D02* 702 | X16780Y16550D01* 703 | X18780Y16550D02* 704 | X18780Y18300D01* 705 | X16780Y21550D02* 706 | X16780Y19800D01* 707 | X18780Y21550D02* 708 | X18780Y19800D01* 709 | X23860Y21550D02* 710 | X21860Y21550D01* 711 | X21860Y16550D02* 712 | X23860Y16550D01* 713 | X21860Y18300D02* 714 | X21860Y16550D01* 715 | X23860Y16550D02* 716 | X23860Y18300D01* 717 | X21860Y21550D02* 718 | X21860Y19800D01* 719 | X23860Y21550D02* 720 | X23860Y19800D01* 721 | X28940Y21550D02* 722 | X26940Y21550D01* 723 | X26940Y16550D02* 724 | X28940Y16550D01* 725 | X26940Y18300D02* 726 | X26940Y16550D01* 727 | X28940Y16550D02* 728 | X28940Y18300D01* 729 | X26940Y21550D02* 730 | X26940Y19800D01* 731 | X28940Y21550D02* 732 | X28940Y19800D01* 733 | X34020Y21550D02* 734 | X32020Y21550D01* 735 | X32020Y16550D02* 736 | X34020Y16550D01* 737 | X32020Y18300D02* 738 | X32020Y16550D01* 739 | X34020Y16550D02* 740 | X34020Y18300D01* 741 | X32020Y21550D02* 742 | X32020Y19800D01* 743 | X34020Y21550D02* 744 | X34020Y19800D01* 745 | X39100Y21550D02* 746 | X37100Y21550D01* 747 | X37100Y16550D02* 748 | X39100Y16550D01* 749 | X37100Y18300D02* 750 | X37100Y16550D01* 751 | X39100Y16550D02* 752 | X39100Y18300D01* 753 | X37100Y21550D02* 754 | X37100Y19800D01* 755 | X39100Y21550D02* 756 | X39100Y19800D01* 757 | X16780Y8930D02* 758 | X18780Y8930D01* 759 | X18780Y13930D02* 760 | X16780Y13930D01* 761 | X18780Y12180D02* 762 | X18780Y13930D01* 763 | X16780Y13930D02* 764 | X16780Y12180D01* 765 | X18780Y8930D02* 766 | X18780Y10680D01* 767 | X16780Y8930D02* 768 | X16780Y10680D01* 769 | X21860Y8930D02* 770 | X23860Y8930D01* 771 | X23860Y13930D02* 772 | X21860Y13930D01* 773 | X23860Y12180D02* 774 | X23860Y13930D01* 775 | X21860Y13930D02* 776 | X21860Y12180D01* 777 | X23860Y8930D02* 778 | X23860Y10680D01* 779 | X21860Y8930D02* 780 | X21860Y10680D01* 781 | X26940Y8930D02* 782 | X28940Y8930D01* 783 | X28940Y13930D02* 784 | X26940Y13930D01* 785 | X28940Y12180D02* 786 | X28940Y13930D01* 787 | X26940Y13930D02* 788 | X26940Y12180D01* 789 | X28940Y8930D02* 790 | X28940Y10680D01* 791 | X26940Y8930D02* 792 | X26940Y10680D01* 793 | X32020Y8930D02* 794 | X34020Y8930D01* 795 | X34020Y13930D02* 796 | X32020Y13930D01* 797 | X34020Y12180D02* 798 | X34020Y13930D01* 799 | X32020Y13930D02* 800 | X32020Y12180D01* 801 | X34020Y8930D02* 802 | X34020Y10680D01* 803 | X32020Y8930D02* 804 | X32020Y10680D01* 805 | X37100Y8930D02* 806 | X39100Y8930D01* 807 | X39100Y13930D02* 808 | X37100Y13930D01* 809 | X39100Y12180D02* 810 | X39100Y13930D01* 811 | X37100Y13930D02* 812 | X37100Y12180D01* 813 | X39100Y8930D02* 814 | X39100Y10680D01* 815 | X37100Y8930D02* 816 | X37100Y10680D01* 817 | G54D13* 818 | X17180Y22050D02* 819 | X18380Y22050D01* 820 | X22260Y22050D02* 821 | X23460Y22050D01* 822 | X27340Y22050D02* 823 | X28540Y22050D01* 824 | X32420Y22050D02* 825 | X33620Y22050D01* 826 | X37500Y22050D02* 827 | X38700Y22050D01* 828 | X18380Y8430D02* 829 | X17180Y8430D01* 830 | X23460Y8430D02* 831 | X22260Y8430D01* 832 | X28540Y8430D02* 833 | X27340Y8430D01* 834 | X33620Y8430D02* 835 | X32420Y8430D01* 836 | X38700Y8430D02* 837 | X37500Y8430D01* 838 | G54D14* 839 | X18580Y24800D02* 840 | X18580Y26000D01* 841 | X16980Y24800D02* 842 | X16980Y26000D01* 843 | X16980Y24800D02* 844 | X16980Y26000D01* 845 | X23660Y24800D02* 846 | X23660Y26000D01* 847 | X22060Y24800D02* 848 | X22060Y26000D01* 849 | X22060Y24800D02* 850 | X22060Y26000D01* 851 | X28740Y24800D02* 852 | X28740Y26000D01* 853 | X27140Y24800D02* 854 | X27140Y26000D01* 855 | X27140Y24800D02* 856 | X27140Y26000D01* 857 | X33820Y24800D02* 858 | X33820Y26000D01* 859 | X32220Y24800D02* 860 | X32220Y26000D01* 861 | X32220Y24800D02* 862 | X32220Y26000D01* 863 | X38900Y24800D02* 864 | X38900Y26000D01* 865 | X37300Y24800D02* 866 | X37300Y26000D01* 867 | X37300Y24800D02* 868 | X37300Y26000D01* 869 | X16980Y5680D02* 870 | X16980Y4480D01* 871 | X18580Y5680D02* 872 | X18580Y4480D01* 873 | X18580Y5680D02* 874 | X18580Y4480D01* 875 | X22060Y5680D02* 876 | X22060Y4480D01* 877 | X23660Y5680D02* 878 | X23660Y4480D01* 879 | X23660Y5680D02* 880 | X23660Y4480D01* 881 | X27140Y5680D02* 882 | X27140Y4480D01* 883 | X28740Y5680D02* 884 | X28740Y4480D01* 885 | X28740Y5680D02* 886 | X28740Y4480D01* 887 | X32220Y5680D02* 888 | X32220Y4480D01* 889 | X33820Y5680D02* 890 | X33820Y4480D01* 891 | X33820Y5680D02* 892 | X33820Y4480D01* 893 | X37300Y5680D02* 894 | X37300Y4480D01* 895 | X38900Y5680D02* 896 | X38900Y4480D01* 897 | X38900Y5680D02* 898 | X38900Y4480D01* 899 | X14150Y17480D02* 900 | X14150Y18080D01* 901 | X11250Y18080D02* 902 | X11250Y17480D01* 903 | X14150Y17480D01* 904 | X14150Y18080D02* 905 | X11250Y18080D01* 906 | X12700Y10880D02* 907 | X12700Y11980D01* 908 | X12700Y10880D02* 909 | X12700Y11980D01* 910 | X43180Y19600D02* 911 | X43180Y18500D01* 912 | X43180Y19600D02* 913 | X43180Y18500D01* 914 | X41730Y13000D02* 915 | X41730Y12400D01* 916 | X44630Y12400D02* 917 | X44630Y13000D01* 918 | X41730Y13000D01* 919 | X41730Y12400D02* 920 | X44630Y12400D01* 921 | G54D15* 922 | X10825Y16380D02* 923 | G75* 924 | D01* 925 | G02X10825Y16380I125J0* 926 | G01* 927 | X44805Y14100D02* 928 | G75* 929 | D01* 930 | G02X44805Y14100I125J0* 931 | G01* 932 | 933 | M02* 934 | -------------------------------------------------------------------------------- /Hardware/LED light setup/LED PCB designs/Design1 - Solder Mask Bottom.gbr: -------------------------------------------------------------------------------- 1 | G04 Generated by Ultiboard 14.0 * 2 | %FSLAX33Y33*% 3 | %MOMM*% 4 | 5 | %ADD10C,0.001*% 6 | %ADD11C,3.300*% 7 | %ADD12C,1.600*% 8 | 9 | 10 | G04 ColorRGB 9900CC for the following layer * 11 | %LNSolder Mask Bottom*% 12 | %LPD*% 13 | G54D10* 14 | G54D11* 15 | X10160Y25400D03* 16 | X10160Y5080D03* 17 | X45720Y25400D03* 18 | X45720Y5080D03* 19 | G54D12* 20 | X5080Y19050D03* 21 | X5080Y13970D03* 22 | X5080Y16510D03* 23 | X5080Y11430D03* 24 | X50800Y16510D03* 25 | X50800Y11430D03* 26 | X50800Y19050D03* 27 | X50800Y13970D03* 28 | 29 | M02* 30 | -------------------------------------------------------------------------------- /Hardware/LED light setup/LED PCB designs/Design1 - Solder Mask Top.gbr: -------------------------------------------------------------------------------- 1 | G04 Generated by Ultiboard 14.0 * 2 | %FSLAX33Y33*% 3 | %MOMM*% 4 | 5 | %ADD10C,0.001*% 6 | %ADD11C,3.300*% 7 | %ADD12R,1.500X1.500*% 8 | %ADD13R,1.750X1.050*% 9 | %ADD14R,0.600X1.050*% 10 | %ADD15R,1.050X1.400*% 11 | %ADD16C,1.600*% 12 | 13 | 14 | G04 ColorRGB FF00CC for the following layer * 15 | %LNSolder Mask Top*% 16 | %LPD*% 17 | G54D10* 18 | G54D11* 19 | X10160Y25400D03* 20 | X10160Y5080D03* 21 | X45720Y25400D03* 22 | X45720Y5080D03* 23 | G54D12* 24 | X17780Y20600D03* 25 | X17780Y17500D03* 26 | X22860Y20600D03* 27 | X22860Y17500D03* 28 | X27940Y20600D03* 29 | X27940Y17500D03* 30 | X33020Y20600D03* 31 | X33020Y17500D03* 32 | X38100Y20600D03* 33 | X38100Y17500D03* 34 | X17780Y12980D03* 35 | X17780Y9880D03* 36 | X22860Y12980D03* 37 | X22860Y9880D03* 38 | X27940Y12980D03* 39 | X27940Y9880D03* 40 | X33020Y12980D03* 41 | X33020Y9880D03* 42 | X38100Y12980D03* 43 | X38100Y9880D03* 44 | G54D13* 45 | X17780Y23900D03* 46 | X17780Y26900D03* 47 | X22860Y23900D03* 48 | X22860Y26900D03* 49 | X27940Y23900D03* 50 | X27940Y26900D03* 51 | X33020Y23900D03* 52 | X33020Y26900D03* 53 | X38100Y23900D03* 54 | X38100Y26900D03* 55 | X17780Y3580D03* 56 | X17780Y6580D03* 57 | X22860Y3580D03* 58 | X22860Y6580D03* 59 | X27940Y3580D03* 60 | X27940Y6580D03* 61 | X33020Y3580D03* 62 | X33020Y6580D03* 63 | X38100Y3580D03* 64 | X38100Y6580D03* 65 | G54D14* 66 | X11750Y16630D03* 67 | X13650Y16630D03* 68 | X12700Y18930D03* 69 | X43180Y11550D03* 70 | X42230Y13850D03* 71 | X44130Y13850D03* 72 | G54D15* 73 | X11750Y11430D03* 74 | X13650Y11430D03* 75 | X42230Y19050D03* 76 | X44130Y19050D03* 77 | G54D16* 78 | X5080Y19050D03* 79 | X5080Y13970D03* 80 | X5080Y16510D03* 81 | X5080Y11430D03* 82 | X50800Y16510D03* 83 | X50800Y11430D03* 84 | X50800Y19050D03* 85 | X50800Y13970D03* 86 | 87 | M02* 88 | -------------------------------------------------------------------------------- /Hardware/LED light setup/laser cutter/COPYRIGHT NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Gregory Pask, Marcy Kittredge, Owen Meng, and Matt Lamparter 2022. 2 | 3 | This source describes Open Hardware and is licensed under the CERNOHL-S v2. 4 | 5 | You may redistribute and modify this source and make products using it 6 | under the terms of the CERN-OHL-S v2 7 | (https://ohwr.org/cern ohl s v2.txt ). 8 | 9 | This source is distributed WITHOUT ANY EXPRESS OR IMPLIED 10 | WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY 11 | QUALITY AND FITNESS FOR A PARTICULAR PURPOSE. Please see 12 | the CERN-OHL-S v2 for applicable conditions. 13 | 14 | Source location: https://github.com/gpask/PiSpy 15 | 16 | As per CERN-OHL-S v2 section 4, should You produce hardware based 17 | on this source, You must where practicable maintain the Source Location 18 | visible on the external case of the PiSpy or other products you make using 19 | this source. 20 | -------------------------------------------------------------------------------- /Hardware/LED light setup/laser cutter/LED diffuser.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 16 | 17 | 18 | 19 | 20 | 22 | 24 | 26 | 28 | 29 | PiSpy 30 | Licensed under CERN-OHL-S v2 31 | Source location https://github.com/gpask/PiSpy 32 | 33 | 34 | 35 | 39 | 45 | 46 | 47 | 49 | US002095 50 | 51 | 52 | -------------------------------------------------------------------------------- /Hardware/LICENSE: -------------------------------------------------------------------------------- 1 | CERN Open Hardware Licence Version 2 - Strongly Reciprocal 2 | 3 | 4 | Preamble 5 | 6 | CERN has developed this licence to promote collaboration among 7 | hardware designers and to provide a legal tool which supports the 8 | freedom to use, study, modify, share and distribute hardware designs 9 | and products based on those designs. Version 2 of the CERN Open 10 | Hardware Licence comes in three variants: CERN-OHL-P (permissive); and 11 | two reciprocal licences: CERN-OHL-W (weakly reciprocal) and this 12 | licence, CERN-OHL-S (strongly reciprocal). 13 | 14 | The CERN-OHL-S is copyright CERN 2020. Anyone is welcome to use it, in 15 | unmodified form only. 16 | 17 | Use of this Licence does not imply any endorsement by CERN of any 18 | Licensor or their designs nor does it imply any involvement by CERN in 19 | their development. 20 | 21 | 22 | 1 Definitions 23 | 24 | 1.1 'Licence' means this CERN-OHL-S. 25 | 26 | 1.2 'Compatible Licence' means 27 | 28 | a) any earlier version of the CERN Open Hardware licence, or 29 | 30 | b) any version of the CERN-OHL-S, or 31 | 32 | c) any licence which permits You to treat the Source to which 33 | it applies as licensed under CERN-OHL-S provided that on 34 | Conveyance of any such Source, or any associated Product You 35 | treat the Source in question as being licensed under 36 | CERN-OHL-S. 37 | 38 | 1.3 'Source' means information such as design materials or digital 39 | code which can be applied to Make or test a Product or to 40 | prepare a Product for use, Conveyance or sale, regardless of its 41 | medium or how it is expressed. It may include Notices. 42 | 43 | 1.4 'Covered Source' means Source that is explicitly made available 44 | under this Licence. 45 | 46 | 1.5 'Product' means any device, component, work or physical object, 47 | whether in finished or intermediate form, arising from the use, 48 | application or processing of Covered Source. 49 | 50 | 1.6 'Make' means to create or configure something, whether by 51 | manufacture, assembly, compiling, loading or applying Covered 52 | Source or another Product or otherwise. 53 | 54 | 1.7 'Available Component' means any part, sub-assembly, library or 55 | code which: 56 | 57 | a) is licensed to You as Complete Source under a Compatible 58 | Licence; or 59 | 60 | b) is available, at the time a Product or the Source containing 61 | it is first Conveyed, to You and any other prospective 62 | licensees 63 | 64 | i) as a physical part with sufficient rights and 65 | information (including any configuration and 66 | programming files and information about its 67 | characteristics and interfaces) to enable it either to 68 | be Made itself, or to be sourced and used to Make the 69 | Product; or 70 | ii) as part of the normal distribution of a tool used to 71 | design or Make the Product. 72 | 73 | 1.8 'Complete Source' means the set of all Source necessary to Make 74 | a Product, in the preferred form for making modifications, 75 | including necessary installation and interfacing information 76 | both for the Product, and for any included Available Components. 77 | If the format is proprietary, it must also be made available in 78 | a format (if the proprietary tool can create it) which is 79 | viewable with a tool available to potential licensees and 80 | licensed under a licence approved by the Free Software 81 | Foundation or the Open Source Initiative. Complete Source need 82 | not include the Source of any Available Component, provided that 83 | You include in the Complete Source sufficient information to 84 | enable a recipient to Make or source and use the Available 85 | Component to Make the Product. 86 | 87 | 1.9 'Source Location' means a location where a Licensor has placed 88 | Covered Source, and which that Licensor reasonably believes will 89 | remain easily accessible for at least three years for anyone to 90 | obtain a digital copy. 91 | 92 | 1.10 'Notice' means copyright, acknowledgement and trademark notices, 93 | Source Location references, modification notices (subsection 94 | 3.3(b)) and all notices that refer to this Licence and to the 95 | disclaimer of warranties that are included in the Covered 96 | Source. 97 | 98 | 1.11 'Licensee' or 'You' means any person exercising rights under 99 | this Licence. 100 | 101 | 1.12 'Licensor' means a natural or legal person who creates or 102 | modifies Covered Source. A person may be a Licensee and a 103 | Licensor at the same time. 104 | 105 | 1.13 'Convey' means to communicate to the public or distribute. 106 | 107 | 108 | 2 Applicability 109 | 110 | 2.1 This Licence governs the use, copying, modification, Conveying 111 | of Covered Source and Products, and the Making of Products. By 112 | exercising any right granted under this Licence, You irrevocably 113 | accept these terms and conditions. 114 | 115 | 2.2 This Licence is granted by the Licensor directly to You, and 116 | shall apply worldwide and without limitation in time. 117 | 118 | 2.3 You shall not attempt to restrict by contract or otherwise the 119 | rights granted under this Licence to other Licensees. 120 | 121 | 2.4 This Licence is not intended to restrict fair use, fair dealing, 122 | or any other similar right. 123 | 124 | 125 | 3 Copying, Modifying and Conveying Covered Source 126 | 127 | 3.1 You may copy and Convey verbatim copies of Covered Source, in 128 | any medium, provided You retain all Notices. 129 | 130 | 3.2 You may modify Covered Source, other than Notices, provided that 131 | You irrevocably undertake to make that modified Covered Source 132 | available from a Source Location should You Convey a Product in 133 | circumstances where the recipient does not otherwise receive a 134 | copy of the modified Covered Source. In each case subsection 3.3 135 | shall apply. 136 | 137 | You may only delete Notices if they are no longer applicable to 138 | the corresponding Covered Source as modified by You and You may 139 | add additional Notices applicable to Your modifications. 140 | Including Covered Source in a larger work is modifying the 141 | Covered Source, and the larger work becomes modified Covered 142 | Source. 143 | 144 | 3.3 You may Convey modified Covered Source (with the effect that You 145 | shall also become a Licensor) provided that You: 146 | 147 | a) retain Notices as required in subsection 3.2; 148 | 149 | b) add a Notice to the modified Covered Source stating that You 150 | have modified it, with the date and brief description of how 151 | You have modified it; 152 | 153 | c) add a Source Location Notice for the modified Covered Source 154 | if You Convey in circumstances where the recipient does not 155 | otherwise receive a copy of the modified Covered Source; and 156 | 157 | d) license the modified Covered Source under the terms and 158 | conditions of this Licence (or, as set out in subsection 159 | 8.3, a later version, if permitted by the licence of the 160 | original Covered Source). Such modified Covered Source must 161 | be licensed as a whole, but excluding Available Components 162 | contained in it, which remain licensed under their own 163 | applicable licences. 164 | 165 | 166 | 4 Making and Conveying Products 167 | 168 | You may Make Products, and/or Convey them, provided that You either 169 | provide each recipient with a copy of the Complete Source or ensure 170 | that each recipient is notified of the Source Location of the Complete 171 | Source. That Complete Source is Covered Source, and You must 172 | accordingly satisfy Your obligations set out in subsection 3.3. If 173 | specified in a Notice, the Product must visibly and securely display 174 | the Source Location on it or its packaging or documentation in the 175 | manner specified in that Notice. 176 | 177 | 178 | 5 Research and Development 179 | 180 | You may Convey Covered Source, modified Covered Source or Products to 181 | a legal entity carrying out development, testing or quality assurance 182 | work on Your behalf provided that the work is performed on terms which 183 | prevent the entity from both using the Source or Products for its own 184 | internal purposes and Conveying the Source or Products or any 185 | modifications to them to any person other than You. Any modifications 186 | made by the entity shall be deemed to be made by You pursuant to 187 | subsection 3.2. 188 | 189 | 190 | 6 DISCLAIMER AND LIABILITY 191 | 192 | 6.1 DISCLAIMER OF WARRANTY -- The Covered Source and any Products 193 | are provided 'as is' and any express or implied warranties, 194 | including, but not limited to, implied warranties of 195 | merchantability, of satisfactory quality, non-infringement of 196 | third party rights, and fitness for a particular purpose or use 197 | are disclaimed in respect of any Source or Product to the 198 | maximum extent permitted by law. The Licensor makes no 199 | representation that any Source or Product does not or will not 200 | infringe any patent, copyright, trade secret or other 201 | proprietary right. The entire risk as to the use, quality, and 202 | performance of any Source or Product shall be with You and not 203 | the Licensor. This disclaimer of warranty is an essential part 204 | of this Licence and a condition for the grant of any rights 205 | granted under this Licence. 206 | 207 | 6.2 EXCLUSION AND LIMITATION OF LIABILITY -- The Licensor shall, to 208 | the maximum extent permitted by law, have no liability for 209 | direct, indirect, special, incidental, consequential, exemplary, 210 | punitive or other damages of any character including, without 211 | limitation, procurement of substitute goods or services, loss of 212 | use, data or profits, or business interruption, however caused 213 | and on any theory of contract, warranty, tort (including 214 | negligence), product liability or otherwise, arising in any way 215 | in relation to the Covered Source, modified Covered Source 216 | and/or the Making or Conveyance of a Product, even if advised of 217 | the possibility of such damages, and You shall hold the 218 | Licensor(s) free and harmless from any liability, costs, 219 | damages, fees and expenses, including claims by third parties, 220 | in relation to such use. 221 | 222 | 223 | 7 Patents 224 | 225 | 7.1 Subject to the terms and conditions of this Licence, each 226 | Licensor hereby grants to You a perpetual, worldwide, 227 | non-exclusive, no-charge, royalty-free, irrevocable (except as 228 | stated in subsections 7.2 and 8.4) patent licence to Make, have 229 | Made, use, offer to sell, sell, import, and otherwise transfer 230 | the Covered Source and Products, where such licence applies only 231 | to those patent claims licensable by such Licensor that are 232 | necessarily infringed by exercising rights under the Covered 233 | Source as Conveyed by that Licensor. 234 | 235 | 7.2 If You institute patent litigation against any entity (including 236 | a cross-claim or counterclaim in a lawsuit) alleging that the 237 | Covered Source or a Product constitutes direct or contributory 238 | patent infringement, or You seek any declaration that a patent 239 | licensed to You under this Licence is invalid or unenforceable 240 | then any rights granted to You under this Licence shall 241 | terminate as of the date such process is initiated. 242 | 243 | 244 | 8 General 245 | 246 | 8.1 If any provisions of this Licence are or subsequently become 247 | invalid or unenforceable for any reason, the remaining 248 | provisions shall remain effective. 249 | 250 | 8.2 You shall not use any of the name (including acronyms and 251 | abbreviations), image, or logo by which the Licensor or CERN is 252 | known, except where needed to comply with section 3, or where 253 | the use is otherwise allowed by law. Any such permitted use 254 | shall be factual and shall not be made so as to suggest any kind 255 | of endorsement or implication of involvement by the Licensor or 256 | its personnel. 257 | 258 | 8.3 CERN may publish updated versions and variants of this Licence 259 | which it considers to be in the spirit of this version, but may 260 | differ in detail to address new problems or concerns. New 261 | versions will be published with a unique version number and a 262 | variant identifier specifying the variant. If the Licensor has 263 | specified that a given variant applies to the Covered Source 264 | without specifying a version, You may treat that Covered Source 265 | as being released under any version of the CERN-OHL with that 266 | variant. If no variant is specified, the Covered Source shall be 267 | treated as being released under CERN-OHL-S. The Licensor may 268 | also specify that the Covered Source is subject to a specific 269 | version of the CERN-OHL or any later version in which case You 270 | may apply this or any later version of CERN-OHL with the same 271 | variant identifier published by CERN. 272 | 273 | 8.4 This Licence shall terminate with immediate effect if You fail 274 | to comply with any of its terms and conditions. 275 | 276 | 8.5 However, if You cease all breaches of this Licence, then Your 277 | Licence from any Licensor is reinstated unless such Licensor has 278 | terminated this Licence by giving You, while You remain in 279 | breach, a notice specifying the breach and requiring You to cure 280 | it within 30 days, and You have failed to come into compliance 281 | in all material respects by the end of the 30 day period. Should 282 | You repeat the breach after receipt of a cure notice and 283 | subsequent reinstatement, this Licence will terminate 284 | immediately and permanently. Section 6 shall continue to apply 285 | after any termination. 286 | 287 | 8.6 This Licence shall not be enforceable except by a Licensor 288 | acting as such, and third party beneficiary rights are 289 | specifically excluded. 290 | -------------------------------------------------------------------------------- /Hardware/Original PiSpy/COPYRIGHT NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Gregory Pask, Marcy Kittredge, and Thomas Thul 2022. 2 | 3 | This source describes Open Hardware and is licensed under the CERNOHL-S v2. 4 | 5 | You may redistribute and modify this source and make products using it 6 | under the terms of the CERN-OHL-S v2 7 | (https://ohwr.org/cern ohl s v2.txt ). 8 | 9 | This source is distributed WITHOUT ANY EXPRESS OR IMPLIED 10 | WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY 11 | QUALITY AND FITNESS FOR A PARTICULAR PURPOSE. Please see 12 | the CERN-OHL-S v2 for applicable conditions. 13 | 14 | Source location: https://github.com/gpask/PiSpy 15 | 16 | As per CERN-OHL-S v2 section 4, should You produce hardware based 17 | on this source, You must where practicable maintain the Source Location 18 | visible on the external case of the PiSpy or other products you make using 19 | this source. 20 | -------------------------------------------------------------------------------- /Hardware/Original PiSpy/Camera extension.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpask/PiSpy/4395658d6053857533d4da31a2e011c20931ea8d/Hardware/Original PiSpy/Camera extension.STL -------------------------------------------------------------------------------- /Hardware/Original PiSpy/Camera mount.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpask/PiSpy/4395658d6053857533d4da31a2e011c20931ea8d/Hardware/Original PiSpy/Camera mount.stl -------------------------------------------------------------------------------- /Hardware/Original PiSpy/Laser Cut Wooden Stand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | PiSpy 70 | Licensed under CERN-OHL-S v2 71 | Source location https://github.com/gpask/PiSpy 72 | 73 | 74 | 75 | 79 | 85 | 86 | 87 | 89 | US002095 90 | 91 | 92 | -------------------------------------------------------------------------------- /Hardware/Original PiSpy/RPi mount.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpask/PiSpy/4395658d6053857533d4da31a2e011c20931ea8d/Hardware/Original PiSpy/RPi mount.STL -------------------------------------------------------------------------------- /Hardware/alternative designs/antbox from above/COPYRIGHT NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Gregory Pask, Marcy Kittredge, and Thomas Thul 2022. 2 | 3 | This source describes Open Hardware and is licensed under the CERNOHL-S v2. 4 | 5 | You may redistribute and modify this source and make products using it 6 | under the terms of the CERN-OHL-S v2 7 | (https://ohwr.org/cern ohl s v2.txt ). 8 | 9 | This source is distributed WITHOUT ANY EXPRESS OR IMPLIED 10 | WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY 11 | QUALITY AND FITNESS FOR A PARTICULAR PURPOSE. Please see 12 | the CERN-OHL-S v2 for applicable conditions. 13 | 14 | Source location: https://github.com/gpask/PiSpy 15 | 16 | As per CERN-OHL-S v2 section 4, should You produce hardware based 17 | on this source, You must where practicable maintain the Source Location 18 | visible on the external case of the PiSpy or other products you make using 19 | this source. 20 | -------------------------------------------------------------------------------- /Hardware/alternative designs/antbox from above/antbox camera mount.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpask/PiSpy/4395658d6053857533d4da31a2e011c20931ea8d/Hardware/alternative designs/antbox from above/antbox camera mount.STL -------------------------------------------------------------------------------- /Hardware/alternative designs/antbox from above/antbox wooden stand 6mm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | PiSpy 210 | Licensed under CERN-OHL-S v2 211 | Source location https://github.com/gpask/PiSpy 212 | 213 | 214 | 215 | 219 | 225 | 226 | 227 | 229 | US002095 230 | 231 | 232 | -------------------------------------------------------------------------------- /Hardware/alternative designs/lid integration with aerial camera and light/COPYRIGHT NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Gregory Pask, Benjamin Morris, and Eamon McMahon 2022. 2 | 3 | This source describes Open Hardware and is licensed under the CERNOHL-S v2. 4 | 5 | You may redistribute and modify this source and make products using it 6 | under the terms of the CERN-OHL-S v2 7 | (https://ohwr.org/cern ohl s v2.txt ). 8 | 9 | This source is distributed WITHOUT ANY EXPRESS OR IMPLIED 10 | WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY 11 | QUALITY AND FITNESS FOR A PARTICULAR PURPOSE. Please see 12 | the CERN-OHL-S v2 for applicable conditions. 13 | 14 | Source location: https://github.com/gpask/PiSpy 15 | 16 | As per CERN-OHL-S v2 section 4, should You produce hardware based 17 | on this source, You must where practicable maintain the Source Location 18 | visible on the external case of the PiSpy or other products you make using 19 | this source. 20 | -------------------------------------------------------------------------------- /Hardware/alternative designs/lid integration with aerial camera and light/camera and LED board mount_laser cut.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | PiSpy 138 | Licensed under CERN-OHL-S v2 139 | Source location https://github.com/gpask/PiSpy 140 | 141 | 142 | 143 | 147 | 153 | 154 | 155 | 157 | US002095 158 | 159 | 160 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /PiSpy User Manual v1.1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpask/PiSpy/4395658d6053857533d4da31a2e011c20931ea8d/PiSpy User Manual v1.1.pdf -------------------------------------------------------------------------------- /PiSpy desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpask/PiSpy/4395658d6053857533d4da31a2e011c20931ea8d/PiSpy desktop.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PiSpy: An Affordable, Accessible, and Flexible Imaging Platform for the Automated Observation of Organismal Biology and Behavior 2 | 3 |

4 | 5 |

6 | 7 | PiSpy has now been published at PLOS One! Check it out [here](https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0276652)\! 8 | 9 | This project is licensed under [GNU General Public License v3.0](https://github.com/gpask/PiSpy/blob/master/LICENSE)\ 10 | The hardware is licensed under [CERN-OHL-S v2](https://github.com/gpask/PiSpy/blob/master/Hardware/LICENSE)\ 11 | 12 | Creative Commons License
PiSpy by Benjamin I. Morris, Marcy J. Kittredge, Bea Casey, Owen Meng, André Maia Chagas, Matt Lamparter, Thomas Thul, Gregory M. Pask is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
13 | 14 | ![oshw_facts (1)](https://user-images.githubusercontent.com/65040738/173196172-edbf5e26-5b83-4c87-8aaa-ec2ce63943bc.svg) 15 | 16 | ![PiSpy OSHWA](https://user-images.githubusercontent.com/65040738/173195494-ecd6bd5a-ad43-461c-9df3-48389fa06da8.svg)\ 17 | This project is Open Source Hardware Association certified [OSHWA UID US002095](https://certification.oshwa.org/us002095.html) 18 | 19 | 20 | 21 | **Contents:** 22 | * [Summary](#summary) 23 | * [Overview of the PiSpy](#overview-of-the-pispy) 24 | * [Video and time lapse image recording across various scales](#video-and-time-lapse-image-recording-across-various-scales) 25 | * [Custom setup for monitoring ant behavior](#custom-setup-for-monitoring-ant-behavior) 26 | 27 | 28 | ## Summary 29 | A great deal of understanding can be gleaned from direct observation of 30 | organismal growth, development, and behavior. However, direct 31 | observation can be time consuming and influence the organism through 32 | unintentional stimuli. Additionally, video capturing equipment can often 33 | be prohibitively expensive, difficult to modify to one's specific needs, 34 | and may come with unnecessary features. Here, we describe the PiSpy, a 35 | low-cost, automated video acquisition platform that uses a Raspberry Pi 36 | computer and camera to record video or images at specified time 37 | intervals or when externally triggered. All settings and controls, such 38 | as programmable light cycling, are accessible to users with no 39 | programming experience through an easy-to-use graphical user. 40 | Importantly, the entire PiSpy system can be assembled for less than 41 | \$100 using laser-cut and 3D-printed components. We demonstrate the 42 | broad applications and flexibility of the PiSpy across a range of model 43 | and non-model organisms. Designs, instructions, and code can be accessed 44 | through an online repository, where a global community of PiSpy users 45 | can also contribute their own unique customizations and help grow the 46 | community of open-source research solutions. 47 | 48 | ## Overview of the PiSpy 49 | 50 |

51 | Fig. 1 52 |

53 | Shown above is the base model of the PiSpy for simple image or video 54 | capture (Fig. 1a). A 3D printable holder mounts both the Raspberry Pi 55 | and PiCamera to a laser cut wooden frame, allowing for easy height 56 | adjustment (Fig. 1A-B). The stands on the frame are also reversible, and 57 | a 3D-printed ball and socket mounts the PiCamera, allowing for free 58 | rotation (Fig. 1C). Optionally, the Raspberry Pi computer can connect to 59 | input and/or output components, such as a motion sensor or lighting, 60 | respectively. This entire setup can be assembled for less than \$100, or 61 | cheaper if multiple setups are being bought at once or certain features 62 | are omitted (see Bill of Materials in GitHub. A complete user's guide 63 | and assembly manual can be found in the Supplementary Materials of the 64 | published paper and will also be continually updated on the GitHub 65 | repository (https://github.com/gpask/PiSpy). The GUI for the PiSpy, 66 | written in Python3 using the TKinter package, controls the functionality 67 | of the PiSpy. Features include capture mode, timed or input-triggered 68 | capture, light control, and camera resolution (Fig. 1D). For more 69 | advanced controls, such as changing the default image/video name and 70 | storage location or the specific camera settings, instructions are 71 | written in the user's manual for how to edit these in the code itself. 72 | 73 | ## Video and time lapse image recording across various scales 74 | 75 | 76 | Imaging of 10 *D. melanogaster* larval locomotion on an agar plate 77 | provided sufficient resolution for analysis with the ImageJ image 78 | processing software, as shown in the above video (sped up to 5x speed): 79 |

80 | Fig. 2a 81 |

82 | 83 | Video of crayfish behavior when placed in an aquarium allowed for the 84 | observation of subtle movements of the swimmerets and legs, even though 85 | the animals were underwater and being viewed through a plastic 86 | container: 87 |

88 | Fig. 2b 89 |

90 | 91 | Automated image capture also makes the PiSpy an effective device for 92 | capturing and visualizing organismal growth over time. Time-lapse 93 | imaging every 5 minutes (and converted to a video at 225 fps) of various beans (*Phaseolus vulgaris*) growing in clear 94 | planters showed detailed root and shoot growth: 95 |

96 | Fig. 2c 97 |

98 | 99 | At a smaller scale, imaging every 5 minutes (and converted to a video at 90 fps of the soil bacterium *Bacillus mycoides* 100 | captured the growth and expansion process over time: 101 |

102 | Fig. 2c 103 |

104 | 105 | ## Custom setup for monitoring ant behavior 106 | 107 |

108 | Fig. 3a 109 |

110 | 111 | The flexibility of the PiSpy allows it to be customized for more 112 | specific research purposes, as shown above. For example, we have used the PiSpy to 113 | monitor the social behaviors in colonies of the Indian jumping ant, 114 | *Harpegnathos saltator*. Because the ants are housed in nestboxes of a 115 | fixed size, we have modified the wooden frame and mount to enclose the 116 | container to allow for easy overhead recording of the colony, as shown in the image above. 117 | To maintain a light-dark cycle for the ants, we used the LED light 118 | control capabilities of the PiSpy to be able to record social behaviors 119 | throughout the day. Custom LED printed circuit boards (PCBs) can be 120 | connected to the general-purpose input output (GPIO) pin of the 121 | Raspberry Pi and allow for the cycling of white and red lighting. 122 | 123 | The sample videos below show the ability of the PiSpy to record animal behavior 124 | in both day and night, with sufficient resolution to capture specific behaviors 125 | such as antennal dueling (asseen in the daytime recording). 126 | In our experiment, the red light is not detected by the ants but allows 127 | for both day and night imaging. Specific camera settings are used to 128 | record in each different lighting conditions to ensure the desirable 129 | imaging quality. We have programmed in default camera settings for day 130 | and night recording, but the user's manual provides instructions for how 131 | to modify these within the PiSpy code. 132 |

133 | Fig. 3b 134 |

135 |

136 | Fig. 3c 137 |

138 | 139 | The GPIO pins of the Raspberry Pi can also be used to trigger image or 140 | video capture with an external sensor, such as a motion sensor or IR 141 | break beam. In our setup (below), an ant disrupts the IR beam as it walks to a 142 | foraging arena, and again when it returns to the main colony. The physical setup is 143 | shown in the image below, including the LEGO contraption to holding the break beam (right side): 144 |

145 | Fig. 3b 146 |

147 | 148 | A sample video in which ants return to the main colony carrying crickets is shown below: 149 |

150 | Fig. 3c 151 |

152 | 153 | As an alternative to automated recordings at fixed time intervals, triggered recordings such as these 154 | could be used to monitor specific activities, such as feeding patterns 155 | or other behaviors. It is our hope that users will create their own 156 | modifications of the PiSpy hardware and/or software and will share these 157 | for use by other researchers and inspire further customizations, 158 | allowing it to serve as both a general use tool and one that can be 159 | applied to highly specific experimental approaches. 160 | 161 | Contributors to PiSpy: 162 | Benjamin I. Morris, Marcy J. Kittredge, Bea Casey, Owen Meng, André Maia Chagas, Matt Lamparter, Thomas Thul, Gregory M. Pask 163 | 164 | For this README, all videos were converted to GIF using ezgif.com 165 | -------------------------------------------------------------------------------- /Software/PiSpy disk image.md: -------------------------------------------------------------------------------- 1 | Click here to download the [PiSpy disk image](https://drive.google.com/file/d/1WO9Z7CKua31OQFlMTX8l0zChpEt_H-ME/view?usp=sharing). 2 | 3 | -------------------------------------------------------------------------------- /Software/PiSpy/Day_Night_Mode.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Class controlling day/night cycling and camera settings 3 | Copyright (C) 2022 Benjamin Morris, Marcy Kittredge, Bea Casey, Gregory Pask 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License version 3 as published by 7 | the Free Software Foundation. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | ''' 17 | 18 | from datetime import datetime 19 | from picamera import PiCamera 20 | from time import sleep 21 | from timeit import Timer 22 | from datetime import datetime 23 | import RPi.GPIO as GPIO 24 | 25 | 26 | class Day_Night: 27 | 28 | def camDay(self, cam, resolution): #function for daytime camera settings for image recording 29 | cam.resolution = (resolution) #set resolution of the camera 30 | cam.framerate = 15 #set the framerate of the camera 31 | cam.shutter_speed = 40000 #set the shutter speed of the camera 32 | cam.iso = 200 #set the camera iso (sensitivity of image sensor) 33 | 34 | def cam_day(self, cam, resolution, framerate): #function for daytime camera settings for video recording 35 | cam.resolution = (resolution) #set resolution of the camera 36 | cam.framerate = int(framerate) #set the framerate of the camera 37 | cam.shutter_speed = 40000 #set the shutter speed of the camera 38 | cam.iso = 200 #set the camera iso (sensitivity of image sensor) 39 | 40 | def camNight(self, cam, resolution): #function for nighttime camera settings for image recording 41 | cam.resolution = (resolution) #set resolution of the camera 42 | cam.framerate = 15 #set framerate of the camera 43 | cam.shutter_speed = 40000 #set the shutter speed of the camera 44 | cam.brightness = 43 #decrease brightness of camera (improves nighttime image quality) 45 | cam.contrast = -8 #decrease contrast of camera (improves nighttime image quality) 46 | cam.iso = 800 #set the camera iso (sensitivity of image sensor) 47 | 48 | def cam_night(self, cam, resolution, framerate): #function for nighttime camera settings for video recording 49 | cam.resolution = (resolution) #set resolution of the camera 50 | cam.framerate = int(framerate) #set framerate of the camera 51 | cam.shutter_speed = 40000 #set the shutter speed of the camera 52 | cam.brightness = 43 #decrease brightness of camera (improves nighttime image quality) 53 | cam.contrast = -8 #decrease contrast of camera (improves nighttime image quality) 54 | cam.iso = 800 #set the camera iso (sensitivity of image sensor) 55 | 56 | def light_on(self, RONTime, WONTime, ROFFTime, WOFFTime): #helper function for while the light is on 57 | self.lock = 1; # set lock 58 | myTime = datetime.now().strftime('%H:%M') # get current time in hour:minute format 59 | curRONTime = RONTime #red light 60 | curWONTime = WONTime #white light 61 | curROFFTime = ROFFTime 62 | curWOFFTime = WOFFTime 63 | if myTime == curRONTime: # turn on red light 64 | sleep(1) #camera sleeps for one second 65 | self.redLight('on') #turns red light on 66 | if myTime == curWOFFTime: 67 | self.whiteLight('off') #turns white light off 68 | elif myTime == curWONTime: # turn on white light 69 | sleep(1) #camera sleeps for one second 70 | self.whiteLight('on') #turns white light on 71 | if myTime == curROFFTime: 72 | self.redLight('off') #turns red light off 73 | elif myTime == curWOFFTime: #turn off white light 74 | self.whiteLight('off') 75 | elif myTime == curROFFTime: #turn off red light 76 | self.redLight('off') 77 | else: # keep current light on 78 | pass 79 | self.lock = 0 # release lock 80 | 81 | 82 | def whiteLight(self, key): #sets white light 83 | if key == 'on': #if the function is called to turn light on 84 | GPIO.setmode(GPIO.BCM) #set BCM GPIO numbering (how pins are referenced) 85 | GPIO.setwarnings(False) # disables warnings 86 | GPIO.setup(18,GPIO.OUT) #set GPIO 18 as output (output mode) 87 | GPIO.output(18,GPIO.HIGH) #set to 3.3V 88 | elif key == 'off': #if the function is called to turn light off 89 | GPIO.setmode(GPIO.BCM) #set BCM GPIO numbering (how pins are referenced) 90 | GPIO.setwarnings(False) #disables warnings 91 | GPIO.setup(18,GPIO.OUT) # set GPIO 18 as output (output mode) 92 | GPIO.output(18,GPIO.LOW) #set to 0V 93 | 94 | def redLight(self, key): #sets red light 95 | if key == 'on': #if function is called to turn light on 96 | GPIO.setmode(GPIO.BCM) #set BCM GPIO numbering (how pins are referenced) **CHANGE TO GPIO.BOARD- SAFER** 97 | GPIO.setwarnings(False) #disable warnings 98 | GPIO.setup(14,GPIO.OUT) # set GPIO 14 as output (output mode) 99 | GPIO.output(14,GPIO.HIGH) #set to 3.3V 100 | elif key == 'off': #if function is called to turn light off 101 | GPIO.setmode(GPIO.BCM) #set BCM GPIO numbering (how pins are referenced) **CHANGE TO GPIO.BOARD- SAFER** 102 | GPIO.setwarnings(False) #disable warnings 103 | GPIO.setup(14,GPIO.OUT) #set GPIO as output (output mode) 104 | GPIO.output(14,GPIO.LOW) #set to 0V -------------------------------------------------------------------------------- /Software/PiSpy/Image.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Class that controls image capture for the PiSpy 3 | Copyright (C) 2022 Benjamin Morris, Marcy Kittredge, Bea Casey, Gregory Pask 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License version 3 as published by 7 | the Free Software Foundation. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | ''' 17 | 18 | from datetime import datetime 19 | from time import sleep 20 | from picamera import PiCamera 21 | import RPi.GPIO as GPIO 22 | from Day_Night_Mode import * 23 | 24 | 25 | class Image: 26 | 27 | def image_capture(self, ID, resolution): #captures image, using settings based off which lights are on 28 | cam = PiCamera() 29 | lights = Day_Night() #initiates lights class 30 | GPIO.setmode(GPIO.BCM) #set BCM GPIO numbering (how pins are referenced) 31 | GPIO.setup(18,GPIO.OUT) #tells computer that GPIO pins used for red/white lights are outputs 32 | GPIO.setup(14,GPIO.OUT) #tells computer that GPIO pins used for red/white lights are outputs 33 | GPIO.setwarnings(False) # disables warnings 34 | if GPIO.input(14) == 1: #if red lights are on, use night settings 35 | lights.camNight(cam, resolution) 36 | else: #otherwise, use day settings 37 | lights.camDay(cam, resolution) 38 | timestamp = datetime.now().strftime("%Y_%m_%d_%H_%M_%S") #sets variable for current time (to minutes) 39 | time = datetime.now().strftime("%H:%M:%S") #sets variable for current time (to seconds) 40 | print('image captured at ' + time) 41 | timestamp = ID + timestamp 42 | sleep(2) # sleep for 2 seconds to let camera "warmup" 43 | cam.capture("/home/pi/Pictures/{}.jpg".format(timestamp)) #takes image, saves to specified path with the timestamp as the format 44 | cam.stop_preview() #hides preview window 45 | cam.close() #closes camera -------------------------------------------------------------------------------- /Software/PiSpy/Import_Trigger.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Class for triggered recording of images or videos 3 | Copyright (C) 2022 Benjamin Morris, Marcy Kittredge, Bea Casey, Gregory Pask 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License version 3 as published by 7 | the Free Software Foundation. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | ''' 17 | 18 | from Image import * 19 | from Record import * 20 | from Day_Night_Mode import * 21 | import RPi.GPIO as GPIO 22 | from datetime import datetime 23 | from picamera import PiCamera 24 | 25 | class Import_Trigger: #class for triggered recording 26 | 27 | def timed_delay(self, delay, RONTime, WONTime, ROFFTime, WOFFTime): #function used to check for lights on/off while delay is happening 28 | lights = Day_Night() #initiates lights class 29 | for x in range(delay): #every second for the length of the specified delay, checks if lights need to be switched then sleeps for 1 second 30 | lights.light_on(RONTime, WONTime, ROFFTime, WOFFTime) 31 | sleep(1) 32 | 33 | def image_trigger(self, ID, delay, trigger, duration, RONTime, WONTime, ROFFTime, WOFFTime, resolution): #manages sensing and acquisition of images 34 | z = 0 #counter for how many images have been taken 35 | GPIO.setmode(GPIO.BCM) #set BCM GPIO numbering (how pins are referenced) 36 | GPIO.setwarnings(False) # disables warnings 37 | GPIO.setup(trigger, GPIO.IN) # sets GPIO connected to sensor as an input 38 | lights = Day_Night() #initiates lights class 39 | delay = int(delay[0:2])*60 + int(delay[3:5]) #calculates delay in seconds from input string in hh:mm format 40 | duration = int(duration * 60) #converts duration to minutes 41 | end_mins = int(datetime.now().strftime("%M")) + duration 42 | end_hours = int(end_mins // 60) + int(datetime.now().strftime("%H")) #calculate hour of ending time 43 | end_mins = int(end_mins % 60) #calculates minutes of ending time (along with line 24 44 | days = 0 45 | while end_hours >= 24: #sets number of days during which recording will occur 46 | end_hours = end_hours - 24 47 | days += 1 48 | end_hours = str(end_hours) #recalculates hour of ending time after removing days 49 | if len(end_hours) == 1: 50 | end_hours = '0' + end_hours #reformats hours 51 | end_mins = str(end_mins) 52 | if len(end_mins) == 1: 53 | end_mins = '0' + end_mins #reformats minutes 54 | end_time = end_hours + end_mins #sets end time with correct formatting 55 | cam = PiCamera() #initates PiCamera. Recording is managed here to avoid camera needing to sleep after being triggered 56 | if GPIO.input(14) == 1:# if red light is on, use night settings 57 | lights.camNight(cam, resolution) 58 | else:# otherwise, use day settings 59 | lights.camDay(cam, resolution) 60 | while days >= 0: 61 | if datetime.now().strftime('%H%M') == end_time: #if reaches end time but there are still 1 or more days remaining, decrease days by 1 62 | days = days - 1 63 | self.timed_delay(60, RONTime, WONTime, ROFFTime, WOFFTime) #delays for 1 minute to prevent loop from running again 64 | i = GPIO.input(trigger) 65 | lights.light_on(RONTime, WONTime, ROFFTime, WOFFTime) 66 | x =0 67 | j = 0 68 | while j < 750: #remove this while loop if using motion sensor. For a break beam, the output is 0 when the beam is broken and 1 when it is not, but the output is not consistent when it should be 1. This loop creates a threshold to prevent accidental activation 69 | #print(i) #this line can be helpful to make sure the break beam is working correctly (if uncommented, output should be repeatedly printing 1 until break beam is triggered 70 | x = x + i 71 | j += 1 72 | i = GPIO.input(trigger) 73 | #print(GPIO.input(trigger)) 74 | if x==0: #break beam is broken, for motion sensor switch to if i >= 1 (some sensors output a 1 when triggered and some output a 0, for other sensors test the output by uncommenting the above print statement to determine what to use 75 | print('capturing image') 76 | timestamp = datetime.now().strftime("%Y_%m_%d_%H_%M_%S") #sets variable for current time (to seconds) 77 | timestamp = ID + 'image' + str(z).zfill(5) + '_' + timestamp 78 | cam.capture("/home/pi/Pictures/{}.jpg".format(timestamp)) #takes image, saves to specified path with the timestamp as the format 79 | cam.close() #close camera 80 | self.timed_delay(delay, RONTime, WONTime, ROFFTime, WOFFTime) #delays specified amount of time, but still checks if lights need to be switched 81 | cam = PiCamera() #re-opens camera 82 | if GPIO.input(14) == 1:# if red light is on, use night settings 83 | lights.camNight(cam, resolution) 84 | else:# otherwise, use day settings 85 | lights.camDay(cam, resolution) 86 | z += 1 87 | print('ready to capture again') 88 | 89 | 90 | def video_trigger(self, ID, delay, trigger, duration, length, RONTime, WONTime, ROFFTime, WOFFTime, resolution, framerate): #manages sensing and acquisition of videos 91 | z = 0 #counter for how many videos have been recorded 92 | GPIO.setmode(GPIO.BCM) #set BCM GPIO numbering (how pins are referenced) 93 | GPIO.setwarnings(False) # disables warnings 94 | GPIO.setup(trigger, GPIO.IN) # sets GPIO connected to sensor as an input 95 | lights = Day_Night() #initiates lights class 96 | delay = int(delay[0:2])*60 + int(delay[3:5]) #calculates delay in seconds from input string in mm:ss format 97 | length = int(length[0:2])*60 + int(length[3:5]) #calculates video length in seconds from input string in mm:ss format 98 | duration = int(duration * 60) #converts duration to minutes 99 | end_mins = int(datetime.now().strftime("%M")) + duration 100 | end_hours = int(end_mins // 60) + int(datetime.now().strftime("%H")) #calculate hour of ending time 101 | end_mins = int(end_mins % 60)#calculates minutes of ending time (along with line 24 102 | days = 0 103 | while end_hours >= 24: #sets number of days during which recording will occur 104 | end_hours = end_hours - 24 105 | days += 1 106 | end_hours = str(end_hours) #recalculates hour of ending time after removing days 107 | if len(end_hours) == 1: 108 | end_hours = '0' + end_hours #reformats hours 109 | end_mins = str(end_mins) 110 | if len(end_mins) == 1: 111 | end_mins = '0' + end_mins #reformats minutes 112 | end_time = end_hours + end_mins #sets end time with correct formatting 113 | cam = PiCamera() #initates PiCamera. Recording is managed here to avoid camera needing to sleep after being triggered 114 | if GPIO.input(14) == 1: # if red light is on, use night settings 115 | lights.cam_night(cam, resolution, framerate) 116 | else: #otherwise, use day settings 117 | lights.cam_day(cam, resolution, framerate) 118 | while days >= 0: 119 | if datetime.now().strftime('%H%M') == end_time: #if reaches end time but there are still 1 or more days remaining, decrease days by 1 120 | days = days - 1 121 | self.timed_delay(60, RONTime, WONTime, ROFFTime, WOFFTime) #delays for 1 minute to prevent loop from running again 122 | i = GPIO.input(trigger) 123 | lights.light_on(RONTime, WONTime, ROFFTime, WOFFTime) 124 | x =0 125 | j = 0 126 | while j < 750: #remove this while loop if using motion sensor. For a break beam, the output is 0 when the beam is broken and 1 when it is not, but the output is not consistent when it should be 1. This loop creates a threshold to prevent accidental activation 127 | #print(i) #this line can be helpful to make sure the break beam is working correctly (if uncommented, output should be repeatedly printing 1 until break beam is triggered 128 | x = x + i 129 | j += 1 130 | i = GPIO.input(trigger) 131 | if x==0: #break beam is broken, for motion sensor switch to if i >= 1 (some sensors output a 1 when triggered and some output a 0, for other sensors test the output by uncommenting the above print statement to determine what to use 132 | print('capturing video') 133 | timestamp = datetime.now().strftime("%Y_%m_%d_%H_%M_%S") #sets variable for current time (to seconds) 134 | timestamp = ID + 'video' + str(z).zfill(5) + '_' + timestamp 135 | name = "/home/pi/Videos/{}".format(timestamp) 136 | cam.start_recording(name + ".h264".format(timestamp)) #begins recording, saves to specified path with the timestamp as the format 137 | cam.wait_recording(length) #checks for exceptions- if error occurs the recording will stop 138 | cam.stop_recording() #ends the recording. If there is an error it will raise the exception 139 | rate = cam.framerate 140 | cam.close() 141 | print('done recording') 142 | os.system("MP4Box -quiet -add //{name}.h264:fps={rate} //{name}.mp4".format(name = name, rate = rate)) #converts to MP4 using MP4Box. If GPAC cannot be installed, remove this line 143 | os.remove("//{name}.h264".format(name = name)) #removes .h264 file. If GPAC cannot be installed, remove this line 144 | self.timed_delay(delay, RONTime, WONTime, ROFFTime, WOFFTime) #delays specified amount of time, but still checks if lights need to be switched 145 | cam = PiCamera() #re-opens camera 146 | if GPIO.input(14) == 1: # if red light is on, use night settings 147 | lights.cam_night(cam, resolution, framerate) 148 | else: #otherwise, use day settings 149 | lights.cam_day(cam, resolution, framerate) 150 | z += 1 151 | print('ready to record again') 152 | 153 | 154 | -------------------------------------------------------------------------------- /Software/PiSpy/PiSpy.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Class for opening and operating the PiSpy GUI 3 | Copyright (C) 2022 Benjamin Morris, Marcy Kittredge, Bea Casey, Gregory Pask 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License version 3 as published by 7 | the Free Software Foundation. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | ''' 17 | 18 | from tkinter import * 19 | from datetime import datetime 20 | from Time_Lists import * 21 | from Import_Trigger import * 22 | from Image import * 23 | from picamera import PiCamera 24 | import sched 25 | import time as t 26 | 27 | class App: 28 | 29 | def __init__(self, master, title): 30 | self.master = master 31 | self.master.title(title) 32 | self.master.maxsize(2000, 20000) 33 | self.resolution = [1280, 720] #default resolution if none selected 34 | self.framerate = 0 #must be reset if video mode selected 35 | self._setUpDisplay() 36 | self._takeAction() 37 | super(Time_Lists).__init__() 38 | 39 | 40 | 41 | def _takeAction(self): 42 | 43 | def enable_video(): #function to check if video box is checked 44 | if video.get() %2 != 0:#if video box is checked, enable video setting control 45 | self.capture_box.config(state = NORMAL) 46 | self.framerate_box.config(state = NORMAL) 47 | if timedVideoMode.get() %2 != 0: #if timed video selected 48 | self.frequency_box.config(state = NORMAL) 49 | if triggerVideoMode.get() %2 != 0: #if triggered video selected 50 | self.input_trigger_box.config(state = NORMAL) 51 | self.delay_box.config(state = NORMAL) 52 | self.duration_box.config(state = NORMAL) 53 | else: #otherwise, disable video setting controls 54 | self.capture_box.config(state = DISABLED) 55 | self.frequency_box.config(state = DISABLED) 56 | self.duration_box.config(state = DISABLED) 57 | self.input_trigger_box.config(state = DISABLED) 58 | self.delay_box.config(state = DISABLED) 59 | self.framerate_box.config(state = DISABLED) 60 | 61 | video = IntVar() #creates variable to enable video controls 62 | self.video_capture_mode = Checkbutton(self.master, text = "Video Capture Mode", variable=video, command=enable_video) #create video check box 63 | self.video_capture_mode.grid(row=0, column=0, sticky=W) #set location of video check box 64 | 65 | def enable_timed_video(): #checks if timed video selected and enables/disables boxes accordingly 66 | if timedVideoMode.get() %2 != 0 and video.get() %2 != 0: 67 | self.capture_box.config(state = NORMAL) 68 | self.frequency_box.config(state = NORMAL) 69 | self.duration_box.config(state = NORMAL) 70 | else: 71 | self.frequency_box.config(state = DISABLED) 72 | 73 | timedVideoMode = IntVar() #creates variable to enable timed video mode 74 | self.timed_video_capture_mode = Checkbutton(self.master, text = "Timed", variable=timedVideoMode, command=enable_timed_video) #create timed video check box 75 | self.timed_video_capture_mode.grid(row=4, column=0, sticky=W) #set location of timed video check box 76 | 77 | def enable_trigger_video(): #checks if triggered video selected and enables/disables boxes accordingly 78 | if triggerVideoMode.get() %2 != 0 and video.get() %2 != 0: 79 | self.capture_box.config(state = NORMAL) 80 | self.duration_box.config(state = NORMAL) 81 | self.input_trigger_box.config(state = NORMAL) 82 | self.delay_box.config(state = NORMAL) 83 | else: 84 | self.input_trigger_box.config(state = DISABLED) 85 | self.delay_box.config(state = DISABLED) 86 | 87 | triggerVideoMode = IntVar() #creates variable to enable triggered video mode 88 | self.trigger_video_capture_mode = Checkbutton(self.master, text = "Input Trigger", variable=triggerVideoMode, command=enable_trigger_video) #create input trigger video check box 89 | self.trigger_video_capture_mode.grid(row=6, column=0, sticky=W) #set location of triggered video check box 90 | 91 | def enable_image(): #function to check if image mode is selected 92 | if image.get() %2 != 0: #image is selected 93 | if timedImageMode.get() %2 != 0: #timed image is selected 94 | self.i_frequency_box.config(state = NORMAL) 95 | if triggerImageMode.get() %2 != 0: #triggered image is selected 96 | self.i_input_trigger_box.config(state = NORMAL) 97 | self.i_delay_box.config(state = NORMAL) 98 | self.i_duration_box.config(state = NORMAL) 99 | else: #image is not selected 100 | self.i_frequency_box.config(state = DISABLED) 101 | self.i_duration_box.config(state = DISABLED) 102 | self.i_input_trigger_box.config(state = DISABLED) 103 | self.i_delay_box.config(state = DISABLED) 104 | 105 | image = IntVar() #creates variable to enable image mode 106 | self.image_capture_mode = Checkbutton(self.master, text = "Image Capture Mode", variable=image, command=enable_image) #creates check box for image mode 107 | self.image_capture_mode.grid(row=9, column=0, sticky=W) #sets location of image check box 108 | 109 | 110 | def enable_timed_image(): #checks if timed image mode is selected 111 | if timedImageMode.get() %2 != 0 and image.get() %2 != 0: 112 | self.i_frequency_box.config(state = NORMAL) 113 | self.i_duration_box.config(state = NORMAL) 114 | else: 115 | self.i_frequency_box.config(state = DISABLED) 116 | 117 | timedImageMode = IntVar() #creates variable to enable timed image mode 118 | self.timed_image_capture_mode = Checkbutton(self.master, text = "Timed", variable=timedImageMode, command=enable_timed_image) #creates check box for timed image mode 119 | self.timed_image_capture_mode.grid(row=11, column=0, sticky=W) #sets location of check box for timed image mode 120 | 121 | def enable_trigger_image(): #checks if triggered image mode is selected 122 | if triggerImageMode.get() %2 != 0 and image.get() %2 != 0: 123 | self.i_duration_box.config(state = NORMAL) 124 | self.i_input_trigger_box.config(state = NORMAL) 125 | self.i_delay_box.config(state = NORMAL) 126 | else: 127 | self.i_input_trigger_box.config(state = DISABLED) 128 | self.i_delay_box.config(state = DISABLED) 129 | 130 | triggerImageMode = IntVar() #creates variable to enable triggered image mode 131 | self.trigger_video_capture_mode = Checkbutton(self.master, text = "Input Trigger", variable=triggerImageMode, command=enable_trigger_image) #creates check box for triggered image mode 132 | self.trigger_video_capture_mode.grid(row=13, column=0, sticky=W) #sets location of triggered image mode 133 | 134 | def enable_light(): #enables light control boxes if Light Control box is checked 135 | if light.get() % 2 != 0: 136 | self.won_box.config(state = NORMAL) 137 | self.ron_box.config(state = NORMAL) 138 | self.woff_box.config(state = NORMAL) 139 | self.roff_box.config(state = NORMAL) 140 | else: 141 | self.won_box.config(state = DISABLED) 142 | self.ron_box.config(state = DISABLED) 143 | self.woff_box.config(state = DISABLED) 144 | self.roff_box.config(state = DISABLED) 145 | 146 | light = IntVar() #creates variable to enable light control 147 | self.light_control = Checkbutton(self.master, text = "Light Control", variable=light, command=enable_light) #creates check box for light control 148 | self.light_control.grid(row=0, column=4, sticky=W) #positions light control box 149 | 150 | def set_resolution(): #sets camera resolution based on selected box 151 | if resolution1.get() % 2 != 0: 152 | self.resolution = [1920, 1080] 153 | elif resolution2.get() % 2 != 0: 154 | self.resolution = [3280, 2464] 155 | elif resolution3.get() % 2 != 0: 156 | self.resolution = [1640, 1232] 157 | elif resolution4.get() % 2 != 0: 158 | self.resolution = [1640, 922] 159 | elif resolution6.get() % 2 != 0: 160 | self.resolution = [640, 480] 161 | elif resolution5.get() % 2 != 0: 162 | self.resolution = [1280, 720] 163 | else: 164 | self.resolution = [1280, 720] 165 | 166 | resolution1 = IntVar() #along with following lines, creates variables to check for selected resolution 167 | resolution2 = IntVar() 168 | resolution3 = IntVar() 169 | resolution4 = IntVar() 170 | resolution5 = IntVar() 171 | resolution6 = IntVar() 172 | 173 | 174 | self.resolution_description = Label(self.master, text= "Select Camera Resolution:").grid(row = 16, column = 0, sticky = W) 175 | self.resolution1_box = Checkbutton(self.master, text = "1920x1080", variable = resolution1, command = set_resolution).grid(row = 17, column = 0, sticky = W) #along with following lines, creates and sets location of resolution check boxes 176 | self.resolution2_box = Checkbutton(self.master, text = "3280x2464", variable = resolution2, command = set_resolution).grid(row = 18, column = 0, sticky = W) 177 | self.resolution3_box = Checkbutton(self.master, text = "1640x1232", variable = resolution3, command = set_resolution).grid(row = 19, column = 0, sticky = W) 178 | self.resolution4_box = Checkbutton(self.master, text = "1640x922", variable = resolution4, command = set_resolution).grid(row = 17, column = 1, sticky = W) 179 | self.resolution5_box = Checkbutton(self.master, text = "1280x720", variable = resolution5, command = set_resolution).grid(row = 18, column = 1, sticky = W) 180 | self.resolution6_box = Checkbutton(self.master, text = "640x480", variable = resolution6, command = set_resolution).grid(row = 19, column = 1, sticky = W) 181 | 182 | self.framerate_description = Label(self.master, text= "Frame Rate:").grid(row = 2, column = 0, sticky = W) 183 | self.framerate_description1 = Label(self.master, text="(fps)").grid(row=2, column = 2, sticky = W) 184 | self.framerate_box = Entry(self.master, state = DISABLED) #creates framerate selection box 185 | self.framerate_box.grid(row = 2, column = 1, sticky = W) #places framerate selection box 186 | 187 | 188 | times = Time_Lists() #initiates Time_Lists class 189 | lights = Day_Night() #initiates Day_Night class 190 | 191 | def set_on(): #sets screen when recording is started 192 | # disable all buttons and text boxes that can possibly alter program 193 | self.lock = 0 # initialize to free 194 | self.apply_button.config(state=DISABLED) 195 | self.capture_box.config(state=DISABLED) 196 | self.frequency_box.config(state=DISABLED) 197 | self.duration_box.config(state=DISABLED) 198 | time = datetime.now().strftime("%H:%M") # get current system time in hour:minute format 199 | ID = '' 200 | if self.filename_box.get() != '': 201 | ID = self.filename_box.get().replace(" ", "_") + '_' 202 | if timedImageMode.get() == 1 or timedVideoMode.get() == 1: #timed mode selected 203 | if video.get() == 1: #timed video mode selected 204 | self.times = times.createList(time, float(self.duration_box.get()), ((int(self.frequency_box.get()[0:2])*60) + (int(self.frequency_box.get()[3:5])))) # create list of all recording times 205 | self.captureLength = int(self.capture_box.get()[0:2])*60 + int(self.capture_box.get()[3:5]) # record captureLength once initially and store for duration of recordings 206 | elif image.get() == 1: #timed image mode selected 207 | self.image_times = times.createList(time, float(self.i_duration_box.get()), ((int(float(self.i_frequency_box.get()[0:2])*60)) + (int(float(self.i_frequency_box.get()[3:5]))))) # create list of all recording times 208 | RONTime = self.ron_box.get() 209 | WONTime = self.won_box.get() 210 | ROFFTime = self.roff_box.get() 211 | WOFFTime = self.woff_box.get() 212 | if video.get() == 1: 213 | duration = str(self.duration_box.get()) 214 | frequency = str(int(self.frequency_box.get()[0:2])*60 + int(self.frequency_box.get()[3:5])) 215 | self.framerate = self.framerate_box.get() 216 | if image.get() == 1: 217 | i_duration = str(self.i_duration_box.get()) 218 | i_frequency = str(int(self.i_frequency_box.get()[0:2])*60 + int(self.i_frequency_box.get()[3:5])) 219 | root.destroy() #closes GUI window 220 | if video.get() == 1: #print selected settings 221 | print("Chosen settings:") 222 | print("Timed Video Mode") 223 | print("Clip Length: " + str(self.captureLength) + " seconds") 224 | print("Duration: " + duration + " hours") 225 | print("Frequency: " + frequency + " minutes") 226 | print("Camera Resolution: " + str(self.resolution[0]) + "x" + str(self.resolution[1])) 227 | print("Camera Frame Rate: " + str(self.framerate)) 228 | if ID != '': 229 | print("File Identifier: " + ID[:-1]) 230 | if WONTime != '': 231 | print('White lights on at ' + WONTime) 232 | if WOFFTime != '': 233 | print('White lights off at ' + WOFFTime) 234 | if RONTime != '': 235 | print('Red lights on at ' + RONTime) 236 | if ROFFTime != '': 237 | print('Red lights off at ' + ROFFTime) 238 | start_length = len(self.times) 239 | while len(self.times) > 0: #run program with selected settings 240 | s = sched.scheduler(t.time, t.sleep) 241 | light_thread = s.enter(.5, 1, lights.light_on(RONTime, WONTime, ROFFTime, WOFFTime)) 242 | time_thread = s.enter(20.25, 1, times.checkVidTime(str(ID+'video'+str(start_length-len(self.times)).zfill(5)+'_'), self.captureLength, self.resolution, self.framerate)) 243 | 244 | 245 | elif image.get() == 1: #prints selected settings using scheduler 246 | print("Chosen settings:") 247 | print("Timed Image Mode") 248 | print("Duration: " + i_duration + " hours") 249 | print("Frequency: " + i_frequency + " minutes") 250 | print("Camera Resolution: " + str(self.resolution[0]) + "x" + str(self.resolution[1])) 251 | if ID != '': 252 | print("File Identifier: " + ID[:-1]) 253 | if WONTime != '': 254 | print('White lights on at ' + WONTime) 255 | if WOFFTime != '': 256 | print('White lights off at ' + WOFFTime) 257 | if RONTime != '': 258 | print('Red lights on at ' + RONTime) 259 | if ROFFTime != '': 260 | print('Red lights off at ' + ROFFTime) 261 | start_length = len(self.image_times) 262 | while len(self.image_times) > 0: #run program with selected settings using scheduler 263 | s = sched.scheduler(t.time, t.sleep) 264 | image_light_thread = s.enter(.5, 1, lights.light_on(RONTime, WONTime, ROFFTime, WOFFTime)) 265 | image_time_thread = s.enter(20.25, 1, times.checkImageTime(str(ID+'image'+str(start_length-len(self.image_times)).zfill(5)+'_'), self.resolution)) 266 | 267 | elif triggerImageMode.get() == 1 or triggerVideoMode.get() == 1: #triggered mode selected 268 | trigger_mode = Import_Trigger() 269 | if video.get() == 1: #triggered video selected 270 | delay = self.delay_box.get() 271 | input_trigger = int(self.input_trigger_box.get()) 272 | duration = int(self.duration_box.get()) 273 | length = self.capture_box.get() 274 | RONTime = self.ron_box.get() 275 | WONTime = self.won_box.get() 276 | ROFFTime = self.roff_box.get() 277 | WOFFTime = self.woff_box.get() 278 | self.framerate = int(self.framerate_box.get()) 279 | root.destroy() #closes GUI window 280 | 281 | print("Chosen settings:") #print chosen settings 282 | print("Triggered Video Mode") 283 | print("Clip Length: " + str(int(length[0:2])*60 + int(length[3:5])) +" seconds") 284 | print("Duration: " + str(duration) + " hours") 285 | print("Source: GPIO" + str(input_trigger)) 286 | print("Delay: " + str(int(delay[0:2])*60 + int(delay[3:5])) + " seconds") 287 | print("Camera Resolution: " + str(self.resolution[0]) + "x" + str(self.resolution[1])) 288 | print("Camera Frame Rate: " + str(self.framerate)) 289 | if ID != '': 290 | print("File Identifier: " + ID[:-1]) 291 | if WONTime != '': 292 | print('White lights on at ' + WONTime) 293 | if WOFFTime != '': 294 | print('White lights off at ' + WOFFTime) 295 | if RONTime != '': 296 | print('Red lights on at ' + RONTime) 297 | if ROFFTime != '': 298 | print('Red lights off at ' + ROFFTime) 299 | 300 | trigger_mode.video_trigger(ID, delay, input_trigger, duration, length, RONTime, WONTime, ROFFTime, WOFFTime, self.resolution, self.framerate) #run program for triggered video mode 301 | 302 | elif image.get() == 1: #triggered image mode 303 | i_delay = self.i_delay_box.get() 304 | i_input_trigger = int(self.i_input_trigger_box.get()) 305 | i_duration = int(self.i_duration_box.get()) 306 | RONTime = self.ron_box.get() 307 | WONTime = self.won_box.get() 308 | ROFFTime = self.roff_box.get() 309 | WOFFTime = self.woff_box.get() 310 | root.destroy() #closes GUI window 311 | 312 | print("Chosen settings:") #print chosen settings 313 | print("Triggered Image Mode") 314 | print("Duration: " + str(i_duration) + " hours") 315 | print("Source: GPIO" + str(i_input_trigger)) 316 | print("Delay: " + str(int(i_delay[0:2])*60 + int(i_delay[3:5])) + " seconds") 317 | print("Camera Resolution: " + str(self.resolution[0]) + "x" + str(self.resolution[1])) 318 | if ID != '': 319 | print("File Identifier: " + ID[:-1]) 320 | if WONTime != '': 321 | print('White lights on at ' + WONTime) 322 | if WOFFTime != '': 323 | print('White lights off at ' + WOFFTime) 324 | if RONTime != '': 325 | print('Red lights on at ' + RONTime) 326 | if ROFFTime != '': 327 | print('Red lights off at ' + ROFFTime) 328 | 329 | trigger_mode.image_trigger(ID, i_delay, i_input_trigger, i_duration, RONTime, WONTime, ROFFTime, WOFFTime, self.resolution) #run program for triggered video mode 330 | 331 | 332 | 333 | self.apply_button = Button(self.master, text = "Run Program", command = set_on) #creates run program button 334 | self.apply_button.grid(row=14, column= 5, sticky=W) #places run program buttom 335 | 336 | self.filename_description1 = Label(self.master, text="File Identifier:").grid(row=13, column = 4, sticky = E) 337 | self.filename_box = Entry(self.master) #creates framerate selection box 338 | self.filename_box.grid(row = 13, column = 5, sticky = W) #places framerate selection box 339 | 340 | # def cancel_program(): #allows for changes to parameters 341 | # # enable all the buttons and boxes disabled when 'Apply' was clicked 342 | # self.capture_box.delete(0, 'end') 343 | # self.frequency_box.delete(0, 'end') 344 | # self.duration_box.delete(0, 'end') 345 | # self.i_frequency_box.delete(0, 'end') 346 | # self.i_duration_box.delete(0, 'end') 347 | # self.won_box.delete(0, 'end') 348 | # self.ron_box.delete(0, 'end') 349 | # self.woff_box.delete(0, 'end') 350 | # self.roff_box.delete(0, 'end') 351 | # self.capture_box.config(state=DISABLED) 352 | # self.frequency_box.config(state=DISABLED) 353 | # self.duration_box.config(state=DISABLED) 354 | # self.i_frequency_box.config(state=DISABLED) 355 | # self.i_duration_box.config(state=DISABLED) 356 | # self.won_box.config(state=DISABLED) 357 | # self.ron_box.config(state=DISABLED) 358 | # self.woff_box.config(state=DISABLED) 359 | # self.roff_box.config(state=DISABLED) 360 | # self.apply_button.config(state=NORMAL) 361 | # self.video_capture_mode.deselect() 362 | # self.image_capture_mode.deselect() 363 | # self.light_control.deselect() 364 | # print("Stop button was pressed") 365 | # #apply_button.cancel(light_on) # cancel apply_button.repeat(500, light_on) 366 | # #apply_button.cancel(checkTime) # cancel apply_button.repeat(500, checkTime) 367 | # lights.whiteLight('off') #turns off light 368 | # lights.redLight('off') #turns off light 369 | 370 | #self.cancel_button = Button(self.master, text = "Reset", command = cancel_program) 371 | #self.cancel_button.grid(row=17, column= 5, sticky=W) 372 | 373 | def test_red_light(): #test the camera//used for preview 374 | lights.redLight('on') 375 | 376 | self.red_light_button = Button(self.master, text = "Red Light On", command = test_red_light) 377 | self.red_light_button.grid(row=6, column = 5, sticky=W) 378 | 379 | def test_white_light(): #test the camera//used for preview 380 | lights.whiteLight('on') 381 | 382 | self.white_light_button = Button(self.master, text = "White Light On", width = 10, command = test_white_light) 383 | self.white_light_button.grid(row=7, column = 5, sticky=W) 384 | 385 | def lights_off(): #turns off lights 386 | lights.whiteLight('off') 387 | lights.redLight('off') 388 | 389 | self.clear_light_button = Button(self.master, text = "Lights Off", width = 10, command = lights_off) 390 | self.clear_light_button.grid(row=8, column = 5, sticky=W) 391 | 392 | def quick_capture(): #function to capture a single image or video 393 | if image.get()%2 != 0: #if image is selected, will take a picture 394 | picture = Image() 395 | ID = '' 396 | if self.filename_box.get() != '': 397 | ID = self.filename_box.get().replace(" ", "_") + '_' 398 | picture.image_capture(ID, self.resolution) 399 | print("picture taken") 400 | elif video.get()%2 != 0: #if video is selected, will take a video 401 | capture_length = int(self.capture_box.get()[0:2])*60 + int(self.capture_box.get()[3:5]) 402 | self.framerate = int(self.framerate_box.get()) 403 | recording = Record() 404 | ID = '' 405 | if self.filename_box.get() != '': 406 | ID = self.filename_box.get().replace(" ", "_") + '_' 407 | recording.start_record(ID, capture_length, self.resolution, self.framerate) 408 | else: #if none selected, will say so and not record anything 409 | print("no mode selected") 410 | 411 | self.quick_capture_button = Button(self.master, text = "Quick Capture", width = 10, command = quick_capture) #creates button for quick capture 412 | self.quick_capture_button.grid(row=11, column = 5, sticky=W) #places button for quick capture 413 | 414 | 415 | 416 | def test_cam(): #test the camera//used for preview 417 | cam = PiCamera() 418 | if GPIO.input(14) == 1: #if red light is on, use night settings 419 | if self.resolution == [3280,2464]:#3280x2464 doesnt work for preview so scales down to 1640x1232 which has same field of view 420 | lights.camNight(cam, [1640,1232]) 421 | else: 422 | lights.camNight(cam, self.resolution) 423 | else:#use day settings 424 | if self.resolution == [3280,2464]:#3280x2464 doesnt work for preview so scales down to 1640x1232 which has same field of view 425 | lights.camDay(cam, [1640,1232]) 426 | else: 427 | lights.camDay(cam, self.resolution) 428 | cam.preview_fullscreen=True #preview screen is not fullscreen 429 | cam.preview_window=(950, 220, 640, 480) #sets window size for window 430 | cam.start_preview() #opens preview window 431 | if self.preview_length_box.get() != '':#if a preview length is specified 432 | sleep(int(self.preview_length_box.get())) #camera sleeps for specified number of seconds 433 | else:#if no length specified, default is a 10 second preview 434 | sleep(10) 435 | cam.stop_preview() #closes preview window 436 | cam.close() 437 | 438 | self.preview_button = Button(self.master, text = "Preview Camera", command = test_cam) #creates preview camera button 439 | self.preview_button.grid(row=10, column = 5, sticky=W) #places preview camera button 440 | 441 | 442 | def _setUpDisplay(self): #sets up display, creating and placing remaining buttons 443 | GPIO.setmode(GPIO.BCM) #set BCM GPIO numbering (how pins are referenced) 444 | GPIO.setwarnings(False) # disables warnings 445 | GPIO.setup(18,GPIO.OUT) #set GPIO 18 as output (output mode) 446 | GPIO.setup(14,GPIO.OUT) #set GPIO 14 as output (output mode) 447 | 448 | self.preview_length_description = Label(self.master, text= "Preview Length:").grid(row = 10, column = 6, sticky = W) 449 | self.preview_length_description1 = Label(self.master, text="(seconds)").grid(row=10, column = 8, sticky = W) 450 | self.preview_length_box = Entry(self.master) 451 | self.preview_length_box.grid(row = 10, column = 7, sticky = W) 452 | 453 | self.capture_description = Label(self.master, text= "Video Clip Length:").grid(row = 1, column = 0, sticky = W) 454 | self.timecapture_description = Label(self.master, text="(mm:ss)").grid(row=1, column = 2, sticky = W) 455 | 456 | self.frequency_description = Label(self.master, text = "Video Frequency: every").grid(row = 5, column = 0, sticky = W) 457 | self.timefrequency_description = Label(self.master, text = "(hh:mm)").grid(row = 5, column = 2, sticky = W) 458 | 459 | self.duration_description = Label(self.master, text= "Duration:").grid(row = 3, column = 0, sticky = W) 460 | self.timeduration_description = Label(self.master, text = "(hours)").grid(row = 3, column = 2, sticky = W) 461 | 462 | self.input_description = Label(self.master, text= "Input:").grid(row = 7, column = 0, sticky = W) 463 | self.triggerinput_description = Label(self.master, text = "(GPIO# (BCM))").grid(row = 7, column = 2, sticky = W) 464 | 465 | self.delay_description = Label(self.master, text= "Delay:").grid(row = 8, column = 0, sticky = W) 466 | self.triggerinput_description = Label(self.master, text = "(mm:ss)").grid(row = 8, column = 2, sticky = W) 467 | 468 | self.capture_box = Entry(self.master, state=DISABLED) 469 | self.capture_box.grid(row = 1, column = 1, sticky = W) 470 | self.frequency_box = Entry(self.master, state=DISABLED) 471 | self.frequency_box.grid(row = 5, column = 1, sticky = W) 472 | self.duration_box = Entry(self.master, state=DISABLED) 473 | self.duration_box.grid(row = 3, column = 1, sticky = W) 474 | self.input_trigger_box = Entry(self.master, state=DISABLED) 475 | self.input_trigger_box.grid(row = 7, column = 1, sticky = W) 476 | self.delay_box = Entry(self.master, state=DISABLED) 477 | self.delay_box.grid(row = 8, column = 1, sticky = W) 478 | 479 | self.i_frequency_description = Label(self.master, text = "Image Frequency: every").grid(row = 12, column = 0, sticky = W) 480 | self.i_timefrequency_description = Label(self.master, text = "(hh:mm)").grid(row = 12, column = 2, sticky = W) 481 | 482 | self.i_duration_description = Label(self.master, text= "Duration:").grid(row = 10, column = 0, sticky = W) 483 | self.i_timeduration_description = Label(self.master, text = "(hours)").grid(row = 10, column = 2, sticky = W) 484 | 485 | self.i_input_description = Label(self.master, text= "Input:").grid(row = 14, column = 0, sticky = W) 486 | self.i_triggerinput_description = Label(self.master, text = "(GPIO# (BCM))").grid(row = 14, column = 2, sticky = W) 487 | 488 | self.i_delay_description = Label(self.master, text= "Delay:").grid(row = 15, column = 0, sticky = W) 489 | self.i_triggerinput_description = Label(self.master, text = "(mm:ss)").grid(row = 15, column = 2, sticky = W) 490 | 491 | self.i_frequency_box = Entry(self.master, state=DISABLED) 492 | self.i_frequency_box.grid(row = 12, column = 1, sticky = W) 493 | self.i_duration_box = Entry(self.master, state=DISABLED) 494 | self.i_duration_box.grid(row = 10, column = 1, sticky = W) 495 | self.i_input_trigger_box = Entry(self.master, state=DISABLED) 496 | self.i_input_trigger_box.grid(row = 14, column = 1, sticky = W) 497 | self.i_delay_box = Entry(self.master, state=DISABLED) 498 | self.i_delay_box.grid(row = 15, column = 1, sticky = W) 499 | 500 | self.light_on_text = Label(self.master, text = "Light Start Times:").grid(row=1, column=4, sticky=W) 501 | self.light_off_text = Label(self.master, text = "Light Stop Times:").grid(row=1, column=7, sticky=W) 502 | 503 | self.won_box = Entry(self.master, state=DISABLED) 504 | self.won_box.grid(row = 2, column = 5, sticky=N) 505 | self.won_box_description = Label(self.master, text= "White LED ON").grid(row=2, column=4, sticky=W) 506 | self.timewon_description = Label(self.master, text="(hh:mm)").grid(row=2, column=6, sticky=W) 507 | 508 | self.woff_box = Entry(self.master, state=DISABLED) 509 | self.woff_box.grid(row=2, column=8, sticky= N) 510 | self.woff_box_description = Label(self.master, text= "White LED OFF").grid(row=2, column=7, sticky=W) 511 | self.timewoff_description = Label(self.master, text="(hh:mm)").grid(row=2, column=9, sticky=W) 512 | 513 | self.ron_box = Entry(self.master, state=DISABLED) 514 | self.ron_box.grid(row = 4, column = 5, sticky=N) 515 | self.ron_box_description = Label(self.master, text= "Red LED ON").grid(row=4, column=4, sticky=W) 516 | self.timeron_description = Label(self.master, text="(hh:mm)").grid(row=4, column=6, sticky=W) 517 | 518 | self.roff_box = Entry(self.master, state=DISABLED) 519 | self.roff_box.grid(row=4, column=8, sticky= N) 520 | self.roff_box_description = Label(self.master, text= "Red LED OFF").grid(row=4, column=7, sticky=W) 521 | self.timeroff_description = Label(self.master, text="(hh:mm)").grid(row=4, column=9, sticky=W) 522 | 523 | 524 | 525 | root = Tk() 526 | root.geometry("25000x500") 527 | 528 | app = App(root, "PiSpy Control") 529 | root.mainloop() 530 | -------------------------------------------------------------------------------- /Software/PiSpy/Record.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Class to record videos using the PiSpy 3 | Copyright (C) 2022 Benjamin Morris, Marcy Kittredge, Bea Casey, Gregory Pask 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License version 3 as published by 7 | the Free Software Foundation. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | ''' 17 | 18 | from datetime import datetime 19 | from time import sleep 20 | from picamera import PiCamera 21 | import RPi.GPIO as GPIO 22 | from Day_Night_Mode import * 23 | import os as os 24 | 25 | class Record: 26 | 27 | def start_record(self, ID, captureLength, resolution, framerate): #captures video, using settings based off which lights are on 28 | cam = PiCamera() 29 | GPIO.setmode(GPIO.BCM) #set BCM GPIO numbering (how pins are referenced) 30 | GPIO.setwarnings(False) # disables warnings 31 | GPIO.setup(18,GPIO.OUT) #tells computer that GPIO pins used for red/white lights are outputs 32 | GPIO.setup(14,GPIO.OUT) #tells computer that GPIO pins used for red/white lights are outputs 33 | lights = Day_Night() #initiates lights class 34 | if GPIO.input(14) == 1: #if red lights are on, use night settings 35 | lights.cam_night(cam, resolution, framerate) 36 | else: #otherwise, use day settings 37 | lights.cam_day(cam, resolution, framerate) 38 | timestamp = datetime.now().strftime("%Y_%m_%d_%H_%M_%S") #sets variable for current time (to minutes) 39 | timestamp = ID + timestamp 40 | sleep(2) # sleep for 2 seconds to let camera "warmup" 41 | time = datetime.now().strftime("%H:%M:%S") #sets variable for current time (to seconds) 42 | print("Recording began at " + time) #prints to monitor 43 | name = "/home/pi/Videos/{}".format(timestamp) 44 | cam.start_recording(name + ".h264", bitrate = 6000000) #begins recording, saves to specified path with the timestamp as the format. to manually set the bitrate, replace 6000000 with selected value 45 | cam.wait_recording(captureLength) #checks for exceptions- if error occurs the recording will stop, otherwise records for specified length 46 | cam.stop_recording() #ends the recording. If there is an error it will raise the exception 47 | time = datetime.now().strftime("%H:%M:%S") #sets variable for current time (to seconds) 48 | cam.stop_preview() #hides preview window 49 | rate = cam.framerate 50 | cam.close() 51 | os.system("MP4Box -quiet -add //{name}.h264:fps={rate} //{name}.mp4".format(name = name, rate = rate)) #converts to MP4 using MP4Box. If GPAC cannot be installed, remove this line 52 | os.remove("//{name}.h264".format(name = name)) #removes .h264 file. If GPAC cannot be installed, remove this line 53 | print("Recording is finished!") #prints to monitor 54 | 55 | 56 | -------------------------------------------------------------------------------- /Software/PiSpy/Time_Lists.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Class to generate times at which recording will take place 3 | Copyright (C) 2022 Benjamin Morris, Marcy Kittredge, Bea Casey, Gregory Pask 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License version 3 as published by 7 | the Free Software Foundation. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | ''' 17 | 18 | from Image import * 19 | from Record import * 20 | 21 | class Time_Lists: 22 | 23 | def __init__(self): 24 | self.times = list() 25 | self.image_times = list() 26 | self.captureLength = int() 27 | self.lock = int() 28 | self.duration_min = int() 29 | self.frequency_min = int() 30 | 31 | def createList(self, time, hours, freq): #function to create list of times for recording, freq is in minutes 32 | self.times.append(time) #add initial time to list of times 33 | i = 0 #iterator 34 | currentTime = time #set the time to an iterable variable 35 | while i<((hours*(60/freq)-1)): #while i is less than the number of times recorded 36 | if (int(currentTime[3:]) + freq ) < 60: # if not going to pass xx:59 37 | currentTime = (currentTime[0:2] + ":" + str((int(currentTime[3:]) + freq))) #make the time a string 38 | if len(currentTime[3:]) == 1: #check if minutes is single digit 39 | currentTime = currentTime[0:3] + "0" + currentTime[3] #add zero before single digit 40 | self.times.append(currentTime) #add string to the list of times 41 | else: # add an hour 42 | #print(currentTime) 43 | if (int(currentTime[0:2]) + (int(currentTime[3:]) + freq) // 60) > 23: # if past 24:00 44 | extra = (int(currentTime[0:2]) + (int(currentTime[3:]) + freq) // 60) - 24 #calculates how far into the next day the next time is 45 | if extra > 9: #if next recording is after 10:00 46 | hour = str(extra) + ":" 47 | else: 48 | hour = "0" + str(extra) + ":" #if hours is a single digit, add leading zero for correct formatting 49 | if (int(currentTime[3:]) - (freq // 60) * 60 + freq) >= 60: #if passing 24:00 and minutes also passing xx:59 50 | currentTime = hour + str(int(currentTime[3:]) - 60 - (freq // 60) * 60 + freq) #sets next time 51 | else: #passing 24:00 but not xx:59 52 | currentTime = hour + str(int(currentTime[3:]) - (freq // 60) * 60 + freq) #sets next time 53 | if (len(currentTime)==4): #if the minutes over the hour is under 10 eg 00:66, which would be set as 01:6 54 | currentTime = str((currentTime[0:3])) + "0" + str(int(currentTime[3:])) #set currentTime to be in the new hour with appropriate minutes (in above example, would be 01:06 55 | self.times.append(currentTime) #add time to list of times 56 | else: #if going to pass xx:59 and not going to pass 24:00 57 | currentTime = str(int(currentTime[0:2]) + (int(currentTime[3:]) + freq) // 60) + ":" + str(int(currentTime[3:]) - ((int(currentTime[3:]) + freq) // 60) * 60 + freq) #set currentTime to be in the new hour with appropriate minutes 58 | if currentTime[1] == ":": #if the second index of the time string is a colon add a zero in front of first digit 59 | currentTime = "0" + currentTime #add zero in front of the currentTime 60 | if (len(currentTime)==4): #if the minutes over the hour is under 10 eg 00:66 61 | currentTime = str((currentTime[0:3])) + "0" + str(int(currentTime[3:])) #set currentTime to be in the new hour with appropriate minutes 62 | self.times.append(currentTime) #add this time to the list of recorded times 63 | i += 1 #increment i by 1 64 | #print(self.times) #print the list of times to the monitor 65 | return(self.times) #return it to the computer 66 | 67 | 68 | def checkVidTime(self, ID, captureLength, resolution, framerate): #finds the current time 69 | time = datetime.now().strftime("%H:%M") # get current system time in hour:minute format 70 | if time == self.times[0]: # if current system time is next recording time 71 | del self.times[0] # removes current time from list 72 | while(self.lock == 1): #while light is on 73 | pass; 74 | self.apply_video_program(ID, captureLength, resolution, framerate) #call function to start recording 75 | #print(times) 76 | 77 | 78 | def checkImageTime(self, ID, resolution): #finds the current time 79 | time = datetime.now().strftime("%H:%M") # get current system time in hour:minute format 80 | if time == self.times[0]: 81 | del self.times[0] 82 | while(self.lock==1): 83 | pass; 84 | self.apply_image_program(ID, resolution) 85 | 86 | 87 | def apply_video_program(self, ID, captureLength, resolution, framerate): #helper function to start recording 88 | recording = Record() 89 | picture = Image() 90 | recording.start_record(ID, captureLength, resolution, framerate) #calling start_record function 91 | 92 | 93 | def apply_image_program(self, ID, resolution): #helper function to start recording 94 | picture = Image() 95 | timestamp = datetime.now().strftime("%H:%M") #sets variable for current time (to seconds) 96 | picture.image_capture(ID, resolution) -------------------------------------------------------------------------------- /okh-PiSpy.yml: -------------------------------------------------------------------------------- 1 | title: PiSpy 2 | description: An Affordable, Accessible, and Flexible Imaging Platform based on a Raspberry 3 | Pi computer and Pi camera 4 | intended-use: Automated Observation of Organismal Biology and Behavior 5 | keywords: 6 | - video recording 7 | - Raspberry Pi 8 | - automated imaging 9 | - null 10 | project-link: https://github.com/gpask/PiSpy 11 | image: https://user-images.githubusercontent.com/64978673/158457158-29e40e41-570e-40ec-b083-b43d4f998991.png 12 | made: true 13 | made-independently: true 14 | license: 15 | hardware: CERN-OHL-1.2 16 | documentation: CC-BY-SA-2.0 17 | software: GPL-3.0-only 18 | licensor: 19 | name: Greg Pask 20 | affiliation: Middlebury College 21 | email: gpask@middlebury.edu 22 | okh-manifest-version: 1.0.0 23 | date-created: 2022-03-22 24 | date-updated: 2022-03-22 25 | manifest-author: 26 | name: Greg Pask 27 | affiliation: Middlebury College 28 | email: gpask@middlebury.edu 29 | manifest-language: en-US 30 | contact: 31 | name: Greg Pask 32 | affiliation: Middlebury College 33 | email: gpask@middlebury.edu 34 | social: 35 | - platform: Twitter 36 | user-handle: G_Pask 37 | contributors: 38 | - name: Benjamin Morris 39 | - name: Marcy Kittredge 40 | - name: Bea Casey 41 | - name: Owen Meng 42 | - name: André Maia Chagas 43 | - name: Matt Lamparter 44 | - name: Thomas Thul 45 | - name: Gregory Pask 46 | version: "1.0" 47 | development-stage: product 48 | documentation-home: https://github.com/gpask/PiSpy 49 | --------------------------------------------------------------------------------