├── .github └── workflows │ └── TestCompile.yml ├── LICENSE.txt ├── OpenWindowAlarm ├── ATtinySerialOut.h ├── ATtinySerialOut.hpp └── OpenWindowAlarm.ino ├── README.md ├── SetPath.cmd ├── extras └── Arduino-Open-Window-Detector-for-Winter.pdf └── pictures ├── 4Modules.jpg ├── ArduinoDigisparkBoardSelection.png ├── BatteryCase.jpg ├── BoardSettings.png ├── CR2032Back.jpg ├── CR2032Front.jpg ├── CR2032Holder.jpg ├── CR3032.jpg ├── Digispark.jpg ├── Final-Version-Compact.jpg ├── Final-Version-Detail.jpg ├── Final-Version-Detail1.jpg ├── Final-Version-Detail_annotated.jpg ├── Final-VersionAAA.jpg ├── Final-VersionLiPo.jpg ├── LiPo.jpg ├── Loudspeaker1.jpg ├── Loudspeaker2.jpg ├── MiniUSBModule.jpg ├── OpenWindowAlarm.png ├── OpenWindowAlarm1.jpg ├── OpenWindowAlarm2AAA.jpg ├── OpenWindowAlarmLiPo.jpg ├── OpenWindowAlarmLiPo_2.jpg ├── Parts.jpg ├── PartsAll_annotated.jpg ├── Patch.jpg ├── Patch1.jpg ├── PatchAnnotated.jpg ├── PatchWithResetOnBack.jpg ├── ResetConnectionBack.jpg ├── instructables-logo-v2.png └── readme.txt /.github/workflows/TestCompile.yml: -------------------------------------------------------------------------------- 1 | # TestCompile.yml 2 | # Github workflow script to test compile all examples of an Arduino library repository. 3 | # 4 | # Copyright (C) 2020 Armin Joachimsmeyer 5 | # https://github.com/ArminJo/Github-Actions 6 | # 7 | 8 | # This is the name of the workflow, visible on GitHub UI. 9 | name: TestCompile 10 | on: 11 | workflow_dispatch: # To run it manually 12 | description: 'manual build check' 13 | push: # see: https://help.github.com/en/actions/reference/events-that-trigger-workflows#pull-request-event-pull_request 14 | paths: 15 | - '**.ino' 16 | - '**.cpp' 17 | - '**.h' 18 | - '**.hpp' 19 | - '**TestCompile.yml' 20 | pull_request: 21 | paths: 22 | - '**.ino' 23 | - '**.cpp' 24 | - '**.h' 25 | - '**.hpp' 26 | - '**TestCompile.yml' 27 | 28 | jobs: 29 | build: 30 | name: ${{ matrix.arduino-boards-fqbn }} - test compiling examples 31 | 32 | runs-on: ubuntu-latest # I picked Ubuntu to use shell scripts. 33 | 34 | strategy: 35 | matrix: 36 | # The matrix will produce one job for each configuration parameter of type `arduino-boards-fqbn` 37 | # In the Arduino IDE, the fqbn is printed in the first line of the verbose output for compilation as parameter -fqbn=... for the "arduino-builder -dump-prefs" command 38 | # 39 | # Examples: arduino:avr:uno, arduino:avr:leonardo, arduino:avr:nano, arduino:avr:mega 40 | # arduino:sam:arduino_due_x, arduino:samd:arduino_zero_native" 41 | # ATTinyCore:avr:attinyx5:chip=85,clock=1internal, digistump:avr:digispark-tiny, digistump:avr:digispark-pro 42 | # STMicroelectronics:stm32:GenF1:pnum=BLUEPILL_F103C8 43 | # esp8266:esp8266:huzzah:eesz=4M3M,xtal=80, esp32:esp32:featheresp32:FlashFreq=80 44 | # You may add a suffix behind the fqbn with "|" to specify one board for e.g. different compile options like arduino:avr:uno|trace 45 | ############################################################################################################# 46 | arduino-boards-fqbn: 47 | - digistump:avr:digispark-tiny:clock=clock1 48 | - ATTinyCore:avr:attinyx5micr:LTO=enable,sketchclock=1pll 49 | 50 | # Specify parameters for each board. 51 | # With sketches-exclude you may exclude specific examples for a board. Use a comma separated list. 52 | ############################################################################################################# 53 | include: 54 | - arduino-boards-fqbn: digistump:avr:digispark-tiny:clock=clock1 # ATtiny85 board @1 MHz 55 | platform-url: https://raw.githubusercontent.com/ArminJo/DigistumpArduino/master/package_digistump_index.json 56 | 57 | - arduino-boards-fqbn: ATTinyCore:avr:attinyx5micr:LTO=enable,sketchclock=1pll 58 | platform-url: http://drazzy.com/package_drazzy.com_index.json 59 | 60 | # Do not cancel all jobs / architectures if one job fails 61 | fail-fast: false 62 | 63 | steps: 64 | - name: Checkout 65 | uses: actions/checkout@master 66 | 67 | - name: Compile all examples 68 | uses: ArminJo/arduino-test-compile@master 69 | with: 70 | arduino-board-fqbn: ${{ matrix.arduino-boards-fqbn }} 71 | platform-url: ${{ matrix.platform-url }} 72 | 73 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /OpenWindowAlarm/ATtinySerialOut.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ATtinySerialOut.h 3 | * 4 | * Copyright (C) 2015-2024 Armin Joachimsmeyer 5 | * Email: armin.joachimsmeyer@gmail.com 6 | * 7 | * This file is part of TinySerialOut https://github.com/ArminJo/ATtinySerialOut. 8 | * 9 | * TinySerialOut is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 17 | * See the GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | * 22 | */ 23 | 24 | // 25 | // ATMEL ATTINY85 26 | // 27 | // +-\/-+ 28 | // PCINT5/!RESET/ADC0/dW (5) PB5 1| |8 VCC 29 | // USB+ PCINT3/XTAL1/CLKI/!OC1B/ADC3 (3) PB3 2| |7 PB2 (2) SCK/USCK/SCL/ADC1/T0/INT0/PCINT2 / TX Debug output 30 | // USB- PCINT4/XTAL2/CLKO/ OC1B/ADC2 (4) PB4 3| |6 PB1 (1) MISO/DO/AIN1/OC0B/ OC1A/PCINT1 - (Digispark) LED 31 | // GND 4| |5 PB0 (0) MOSI/DI/AIN0/OC0A/!OC1A/SDA/AREF/PCINT0 32 | // +----+ 33 | // ATMEL ATTINY167 34 | // Pin numbers are for Digispark core 35 | // Pin numbers in parenthesis are for ATTinyCore 36 | // 37 | // +-\/-+ 38 | // RX 6 (0) PA0 1| |20 PB0 (D8) 0 OC1AU TONE Timer 1 Channel A 39 | // TX 7 (1) PA1 2| |19 PB1 (9) 1 OC1BU Internal LED 40 | // 8 (2) PA2 3| |18 PB2 (10) 2 OC1AV Timer 1 Channel B 41 | // INT1 9 (3) PA3 4| |17 PB3 (11) 4 OC1BV connected with 51 ohm to D- and 3.3 volt zener diode. 42 | // AVCC 5| |16 GND 43 | // AGND 6| |15 VCC 44 | // 10 (4) PA4 7| |14 PB4 (12) XTAL1 45 | // 11 (5) PA5 8| |13 PB5 (13) XTAL2 46 | // 12 (6) PA6 9| |12 PB6 (14) 3 INT0 connected with 68 ohm to D+ (and disconnected 3.3 volt zener diode). Is terminated with ~20 kOhm if USB attached :-( 47 | // 5 (7) PA7 10| |11 PB7 (15) RESET 48 | // +----+ 49 | // 50 | // MH-ET LIVE Tiny88 (16.0MHz) board 51 | // Digital Pin numbers in parenthesis are for ATTinyCore library 52 | // USB 53 | // +-\__/-+ 54 | // PA2 15| |14 PB7 55 | // PA3 16| |13 PB5 SCK 56 | // D17 PA0 A6| |12 PB4 MISO 57 | // D18 PA1 A7| |11 PB3 MOSI 58 | // (D17) D19 PC0 A0| |10 PB2 OC1B/PWM SS 59 | // (D18) D20 PC1 A1| |9 PB1 OC1A/PWM 60 | // (D19) D21 PC2 A2| |8 PB0 61 | // (D20) D22 PC3 A3| |7 PD7 RX 62 | //SDA (D21) D23 PC4 A4| |6 PD6 TX 63 | //SCL (D22) D24 PC5 A5| |5 PD5 64 | // (D23) D25 PC7 25| |4 PD4 65 | //RESET PC6 RST| |3 PD3 INT1 66 | //LED PD0 0| |5V 67 | //USB+ PD1 1| |GND 68 | //USB- INT0 PD2 2| |VIN 69 | // +------+ 70 | #ifndef _ATTINY_SERIAL_OUT_H 71 | #define _ATTINY_SERIAL_OUT_H 72 | 73 | #if defined(__AVR_ATtiny13__) || defined(__AVR_ATtiny13A__) || defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) \ 74 | || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) \ 75 | || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) \ 76 | || defined(__AVR_ATtiny88__) 77 | #include 78 | 79 | #define VERSION_ATTINY_SERIAL_OUT "2.3.1" 80 | #define VERSION_ATTINY_SERIAL_OUT_MAJOR 2 81 | #define VERSION_ATTINY_SERIAL_OUT_MINOR 3 82 | #define VERSION_ATTINY_SERIAL_OUT_PATCH 1 83 | // The change log is at the bottom of the file 84 | 85 | /* 86 | * Macro to convert 3 version parts into an integer 87 | * To be used in preprocessor comparisons, such as #if VERSION_ATTINY_SERIAL_OUT_HEX >= VERSION_HEX_VALUE(3, 0, 0) 88 | */ 89 | #define VERSION_HEX_VALUE(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) 90 | #define VERSION_ATTINY_SERIAL_OUT_HEX VERSION_HEX_VALUE(VERSION_ATTINY_SERIAL_OUT_MAJOR, VERSION_ATTINY_SERIAL_OUT_MINOR, VERSION_ATTINY_SERIAL_OUT_PATCH) 91 | 92 | #if (F_CPU != 1000000) && (F_CPU != 8000000) && (F_CPU != 16000000) 93 | #error F_CPU value must be 1000000, 8000000 or 16000000. 94 | #endif 95 | 96 | #if !defined(TX_PIN) 97 | # if defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // Digispark PRO board 98 | # if defined PIN_PA1 99 | // ATTinyCore 100 | #define TX_PIN PIN_PA1 // (package pin 2 / TXD on Tiny167) - can use one of PA0 to PA7 here 101 | # elif defined PA1 // 102 | #define TX_PIN PA1 // (package pin 2 / TXD on Tiny167) - can use one of PA0 to PA7 here 103 | # endif 104 | 105 | # elif defined(__AVR_ATtiny88__) // MH-ET LIVE Tiny88(16.0MHz) board 106 | #define TX_PIN PIN_PD6 // (board pin 6) - can use one of PD3 to PD7 here 107 | 108 | # elif defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) 109 | #define TX_PIN PIN_PB2 // Can use one of PIN_PA0 to PIN_PA7 and PIN_PB0 to PIN_PB2 here 110 | 111 | # else// Digispark board 112 | # if defined(DIGISTUMPCORE) 113 | #define TX_PIN PB2 // (package pin 7 on Tiny85) - can use one of PB0 to PB4 (+PB5) here 114 | # else 115 | // ATTinyCore 116 | #define TX_PIN PIN_PB2 // (package pin 7 on Tiny85) - can use one of PB0 to PB4 (+PB5) here 117 | # endif 118 | # endif 119 | #endif 120 | 121 | /* 122 | * Activate this, if you want to save 10 bytes code size and if you can live 123 | * with 87 micro seconds intervals of disabled interrupts for each sent byte @115200 baud. 124 | */ 125 | //#define USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT 126 | /* 127 | * @1 MHz use bigger (+120 bytes for unrolled loop) but faster code. Otherwise only 38400 baud is possible. 128 | * @8/16 MHz use 115200 baud instead of 230400 baud. 129 | */ 130 | //#define TINY_SERIAL_DO_NOT_USE_115200BAUD 131 | #if !defined(TINY_SERIAL_DO_NOT_USE_115200BAUD) // define this to force using other baud rates 132 | #define _USE_115200BAUD // to avoid double negations 133 | #endif 134 | 135 | /* 136 | * If defined, you can use this class as a replacement for standard Serial as a print class e.g. 137 | * for functions where you require a Print class like in void prinInfo(Print *aSerial). Increases program size. 138 | */ 139 | //#define TINY_SERIAL_INHERIT_FROM_PRINT 140 | 141 | // The same class definition as for plain arduino 142 | #if not defined(F) 143 | class __FlashStringHelper; 144 | #define F(string_literal) (reinterpret_cast(PSTR(string_literal))) 145 | #endif 146 | 147 | extern bool sUseCliSeiForWrite; // default is true 148 | void useCliSeiForStrings(bool aUseCliSeiForWrite); // might be useful to set to false if output is done from ISR, to avoid to call unwanted sei(). 149 | 150 | void initTXPin(); // Must be called once if pin is not set to output otherwise 151 | void write1Start8Data1StopNoParity(uint8_t aValue); 152 | void write1Start8Data1StopNoParityWithCliSei(uint8_t aValue); 153 | void writeValue(uint8_t aValue); 154 | 155 | void writeString(const char *aStringPtr); 156 | void writeString(const __FlashStringHelper *aStringPtr); 157 | void writeString_P(const char *aStringPtr); 158 | void writeString_E(const char *aStringPtr); 159 | void writeStringWithCliSei(const char *aStringPtr); 160 | void writeStringWithoutCliSei(const char *aStringPtr); 161 | void writeStringSkipLeadingSpaces(const char *aStringPtr); 162 | 163 | void writeBinary(uint8_t aByte); // write direct without decoding 164 | void writeChar(uint8_t aChar); // Synonym for writeBinary 165 | void writeCRLF(); 166 | void writeByte(int8_t aByte); 167 | void writeUnsignedByte(uint8_t aByte); 168 | void writeUnsignedByteHex(uint8_t aByte); 169 | void writeUnsignedByteHexWithPrefix(uint8_t aByte); 170 | void writeInt(int16_t aInteger); 171 | void writeUnsignedInt(uint16_t aInteger); 172 | void writeLong(int32_t aLong); 173 | void writeUnsignedLong(uint32_t aLong); 174 | void writeFloat(double aFloat); 175 | void writeFloat(double aFloat, uint8_t aDigits); 176 | 177 | char nibbleToHex(uint8_t aByte); 178 | 179 | #if defined(TINY_SERIAL_INHERIT_FROM_PRINT) 180 | class TinySerialOut: public Print 181 | #else 182 | class TinySerialOut 183 | #endif 184 | { 185 | public: 186 | 187 | void begin(long); 188 | void end(); 189 | void flush(void); // not required -> dummy 190 | 191 | void printHex(uint8_t aByte); // with 0x prefix 192 | void printHex(uint16_t aWord); // with 0x prefix 193 | void printlnHex(uint8_t aByte); // with 0x prefix 194 | void printlnHex(uint16_t aWord); // with 0x prefix 195 | 196 | // virtual functions of Print class 197 | size_t write(uint8_t aByte); 198 | operator bool() { return true; } // To support "while (!Serial); // wait for serial port to connect. Required for Leonardo only 199 | 200 | #if !defined(TINY_SERIAL_INHERIT_FROM_PRINT) 201 | void print(const __FlashStringHelper *aStringPtr); 202 | void print(const char *aStringPtr); 203 | void print(char aChar); 204 | void print(uint8_t aByte, uint8_t aBase = 10); 205 | void print(int16_t aInteger, uint8_t aBase = 10); 206 | void print(uint16_t aInteger, uint8_t aBase = 10); 207 | void print(int32_t aLong, uint8_t aBase = 10); 208 | void print(uint32_t aLong, uint8_t aBase = 10); 209 | void print(double aFloat, uint8_t aDigits = 2); 210 | 211 | void println(const char *aStringPtr); 212 | void println(const __FlashStringHelper *aStringPtr); 213 | void println(char aChar); 214 | void println(uint8_t aByte, uint8_t aBase = 10); 215 | void println(int16_t aInteger, uint8_t aBase = 10); 216 | void println(uint16_t aInteger, uint8_t aBase = 10); 217 | void println(int32_t aLong, uint8_t aBase = 10); 218 | void println(uint32_t aLong, uint8_t aBase = 10); 219 | void println(double aFloat, uint8_t aDigits = 2); 220 | 221 | void println(void); 222 | #endif // TINY_SERIAL_INHERIT_FROM_PRINT 223 | 224 | }; 225 | 226 | // This if is required to be compatible with ATTinyCores and AttinyDigisparkCores 227 | #if defined(USE_SOFTWARE_SERIAL) && (7-USE_SOFTWARE_SERIAL-7 == 14) 228 | #define USE_SOFTWARE_SERIAL 1 // define it to 1 if it is only defined, but has no value 229 | #endif 230 | #if defined(DEFAULT_TO_TINY_DEBUG_SERIAL) /*AttinyDigisparkCore condition for defining Serial at line 745 in TinyDebugSerial.h*/ \ 231 | || ((!defined(UBRRH) && !defined(UBRR0H)) || (defined(USE_SOFTWARE_SERIAL) && USE_SOFTWARE_SERIAL)) /*ATTinyCore condition for defining Serial at line 55 in TinySoftwareSerial.h*/\ 232 | || ((defined(UBRRH) || defined(UBRR0H) || (defined(LINBRRH)) && !USE_SOFTWARE_SERIAL)) /*ATTinyCore condition for for defining Serial at line 71ff in HardwareSerial.h*/ 233 | extern TinySerialOut SerialOut; // Name our instance SerialOut since Serial is already declared 234 | #define Serial SerialOut // Redirect all usages of Serial to our SerialOut instance :-) 235 | #else 236 | // No known cores here -> delete possible existent definitions of Serial, before we declare our object (below) 237 | # if defined(Serial) 238 | #undef Serial 239 | # endif 240 | extern TinySerialOut Serial; // if there is no Serial object, we can name the instance of our class Serial :-) 241 | #endif 242 | 243 | #if !defined(TINY_SERIAL_INHERIT_FROM_PRINT) 244 | #define Print TinySerialOut 245 | #endif 246 | 247 | #endif // defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) 248 | 249 | #endif // _ATTINY_SERIAL_OUT_H 250 | -------------------------------------------------------------------------------- /OpenWindowAlarm/ATtinySerialOut.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ATtinySerialOut.hpp 3 | * 4 | * For transmitting debug data over bit bang serial with 115200 baud for 1/8/16 MHz ATtiny clock. 5 | * For 1 MHz you can choose also 38400 baud (120 bytes smaller code size). 6 | * For 8/16 MHz you can choose also 230400 baud (just faster). 7 | * 1 Start, 8 Data, 1 Stop, No Parity 8 | * 9 | * Using PB2 // (Pin7 on Tiny85) as default TX pin to be compatible with digispark board 10 | * To change the output pin, add a line "#define TX_PIN ..." before the line #include "TinySerialOut.hpp" 11 | * or or set it as compiler symbol like "-DTX_PIN PIN_PB1". 12 | * 13 | * Using the Serial.print commands needs 4 bytes extra for each call. 14 | * 15 | * 16 | * Copyright (C) 2015-2024 Armin Joachimsmeyer 17 | * Email: armin.joachimsmeyer@gmail.com 18 | * 19 | * This file is part of TinySerialOut https://github.com/ArminJo/ATtinySerialOut. 20 | * 21 | * TinySerialOut is free software: you can redistribute it and/or modify 22 | * it under the terms of the GNU General Public License as published by 23 | * the Free Software Foundation, either version 3 of the License, or 24 | * (at your option) any later version. 25 | * 26 | * This program is distributed in the hope that it will be useful, 27 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 29 | * See the GNU General Public License for more details. 30 | * 31 | * You should have received a copy of the GNU General Public License 32 | * along with this program. If not, see . 33 | * 34 | */ 35 | 36 | #ifndef _ATTINY_SERIAL_OUT_HPP 37 | #define _ATTINY_SERIAL_OUT_HPP 38 | 39 | #if defined(__AVR_ATtiny13__) || defined(__AVR_ATtiny13A__) || defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) \ 40 | || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) \ 41 | || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) \ 42 | || defined(__AVR_ATtiny88__) 43 | #include "ATtinySerialOut.h" 44 | #include // for eeprom_read_byte() in writeString_E() 45 | 46 | #ifndef _NOP 47 | #define _NOP() __asm__ volatile ("nop") 48 | #endif 49 | 50 | #if !defined(PORTB) 51 | #define PORTB (*(volatile uint8_t *)((0x18) + 0x20)) 52 | #endif 53 | 54 | #if defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) // For use with ATTinyCore 55 | # if TX_PIN == PIN_PA0 || TX_PIN == PIN_PA1 || TX_PIN == PIN_PA2 || TX_PIN == PIN_PA3 \ 56 | || TX_PIN == PIN_PA4 || TX_PIN == PIN_PA5 || TX_PIN == PIN_PA6 || TX_PIN == PIN_PA7 57 | #define TX_PORT PORTA 58 | #define TX_PORT_ADDR 0x02 // from #define PORTA _SFR_IO8(0x02) 59 | #define TX_DDR DDRA 60 | # else 61 | #define TX_PORT PORTB 62 | #define TX_PORT_ADDR 0x05 // from #define PORTB _SFR_IO8(0x05) 63 | #define TX_DDR DDRB 64 | # endif 65 | 66 | #elif defined(__AVR_ATtiny88__) 67 | // MH-ET LIVE Tiny88(16.0MHz) board 68 | # if TX_PIN <= 7 69 | #define TX_PORT PORTD 70 | #define TX_PORT_ADDR 0x0B // from #define PORTD _SFR_IO8(0x0B) 71 | #define TX_DDR DDRD 72 | # elif TX_PIN <= 15 73 | #define TX_PORT PORTB 74 | #define TX_PORT_ADDR 0x05 75 | #define TX_DDR DDRB 76 | # elif TX_PIN <= 22 77 | #define TX_PORT PORTC 78 | #define TX_PORT_ADDR 0x08 79 | #define TX_DDR DDRC 80 | # elif TX_PIN <= 26 81 | #define TX_PORT PORTA 82 | #define TX_PORT_ADDR 0x0E 83 | #define TX_DDR DDRA 84 | # else 85 | #define TX_PORT PORTC 86 | #define TX_PORT_ADDR 0x08 87 | #define TX_DDR DDRC 88 | # endif 89 | 90 | #elif defined(__AVR_ATtiny84__) // For use with ATTinyCore 91 | # if TX_PIN == PIN_PA0 || TX_PIN == PIN_PA1 || TX_PIN == PIN_PA2 || TX_PIN == PIN_PA3 \ 92 | || TX_PIN == PIN_PA4 || TX_PIN == PIN_PA5 || TX_PIN == PIN_PA6 || TX_PIN == PIN_PA7 93 | #define TX_PORT PORTA 94 | #define TX_PORT_ADDR 0x1B 95 | #define TX_DDR DDRA 96 | # else 97 | #define TX_PORT PORTB 98 | #define TX_PORT_ADDR 0x18 99 | #define TX_DDR DDRB 100 | # endif 101 | 102 | #else 103 | // ATtinyX5 here 104 | #define TX_PORT PORTB 105 | #define TX_PORT_ADDR 0x18 // PORTB 106 | #define TX_DDR DDRB 107 | #endif // defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) 108 | 109 | #if defined(digitalPinToPCMSKbit) 110 | #define TX_BIT_NUMBER digitalPinToPCMSKbit(TX_PIN) 111 | #else 112 | #define TX_BIT_NUMBER TX_PIN 113 | #endif 114 | 115 | void write1Start8Data1StopNoParity(uint8_t aValue); 116 | 117 | bool sUseCliSeiForWrite = true; 118 | 119 | /* 120 | * The Serial Instance!!! 121 | */ 122 | // #if ... to be compatible with ATTinyCores and AttinyDigisparkCores 123 | #if (!defined(UBRRH) && !defined(UBRR0H)) /*AttinyDigisparkCore and AttinyDigisparkCore condition*/ \ 124 | || USE_SOFTWARE_SERIAL /*AttinyDigisparkCore condition*/\ 125 | || ((defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(LINBRRH)) && !USE_SOFTWARE_SERIAL)/*AttinyDigisparkCore condition for HardwareSerial*/ 126 | // Switch to SerialOut since Serial is already defined 127 | // or activate line 745 in TinyDebugSerial.h included in AttinyDigisparkCores/src/tiny/WProgram.h at line 24 for AttinyDigisparkCores 128 | TinySerialOut SerialOut; 129 | #else 130 | TinySerialOut Serial; 131 | #endif 132 | 133 | /* 134 | * Must be called once if pin is not set to output otherwise 135 | */ 136 | void initTXPin() { 137 | // TX_PIN is active LOW, so set it to HIGH initially 138 | TX_PORT |= (1 << TX_BIT_NUMBER); 139 | // set pin direction to output 140 | TX_DDR |= (1 << TX_BIT_NUMBER); 141 | } 142 | 143 | void write1Start8Data1StopNoParityWithCliSei(uint8_t aValue) { 144 | uint8_t oldSREG = SREG; 145 | cli(); 146 | write1Start8Data1StopNoParity(aValue); 147 | SREG = oldSREG; 148 | } 149 | 150 | void writeValue(uint8_t aValue) { 151 | write1Start8Data1StopNoParity(aValue); 152 | } 153 | 154 | /* 155 | * Used for writeString() and therefore all write() and print 156 | */ 157 | void useCliSeiForStrings(bool aUseCliSeiForWrite) { 158 | sUseCliSeiForWrite = aUseCliSeiForWrite; 159 | } 160 | 161 | /* 162 | * Write String residing in RAM 163 | */ 164 | void writeString(const char *aStringPtr) { 165 | #if !defined(USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT) 166 | if (sUseCliSeiForWrite) { 167 | #endif 168 | while (*aStringPtr != 0) { 169 | write1Start8Data1StopNoParityWithCliSei(*aStringPtr++); 170 | } 171 | #if !defined(USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT) 172 | } else { 173 | while (*aStringPtr != 0) { 174 | write1Start8Data1StopNoParity(*aStringPtr++); 175 | } 176 | } 177 | #endif 178 | } 179 | 180 | /* 181 | * Write string residing in program memory (FLASH) 182 | */ 183 | void writeString_P(const char *aStringPtr) { 184 | uint8_t tChar = pgm_read_byte((const uint8_t* ) aStringPtr); 185 | // Comparing with 0xFF is safety net for wrong string pointer 186 | while (tChar != 0 && tChar != 0xFF) { 187 | #if defined(USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT) 188 | write1Start8Data1StopNoParityWithCliSei(tChar); 189 | #else 190 | if (sUseCliSeiForWrite) { 191 | write1Start8Data1StopNoParityWithCliSei(tChar); 192 | } else { 193 | write1Start8Data1StopNoParity(tChar); 194 | } 195 | #endif 196 | tChar = pgm_read_byte((const uint8_t* ) ++aStringPtr); 197 | } 198 | } 199 | 200 | /* 201 | * Write string residing in program memory (FLASH) 202 | */ 203 | void writeString(const __FlashStringHelper *aStringPtr) { 204 | PGM_P tPGMStringPtr = reinterpret_cast(aStringPtr); 205 | uint8_t tChar = pgm_read_byte((const uint8_t* ) aStringPtr); 206 | // Comparing with 0xFF is safety net for wrong string pointer 207 | while (tChar != 0 && tChar != 0xFF) { 208 | #if defined(USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT) 209 | write1Start8Data1StopNoParityWithCliSei(tChar); 210 | #else 211 | if (sUseCliSeiForWrite) { 212 | write1Start8Data1StopNoParityWithCliSei(tChar); 213 | } else { 214 | write1Start8Data1StopNoParity(tChar); 215 | } 216 | #endif 217 | tChar = pgm_read_byte((const uint8_t* ) ++tPGMStringPtr); 218 | } 219 | } 220 | 221 | /* 222 | * Write string residing in EEPROM space 223 | */ 224 | void writeString_E(const char *aStringPtr) { 225 | uint8_t tChar = eeprom_read_byte((const uint8_t*) aStringPtr); 226 | // Comparing with 0xFF is safety net for wrong string pointer 227 | while (tChar != 0 && tChar != 0xFF) { 228 | #if defined(USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT) 229 | write1Start8Data1StopNoParityWithCliSei(tChar); 230 | #else 231 | if (sUseCliSeiForWrite) { 232 | write1Start8Data1StopNoParityWithCliSei(tChar); 233 | } else { 234 | write1Start8Data1StopNoParity(tChar); 235 | } 236 | #endif 237 | tChar = eeprom_read_byte((const uint8_t*) ++aStringPtr); 238 | } 239 | } 240 | 241 | void writeStringWithoutCliSei(const char *aStringPtr) { 242 | while (*aStringPtr != 0) { 243 | write1Start8Data1StopNoParity(*aStringPtr++); 244 | } 245 | } 246 | 247 | void writeStringWithCliSei(const char *aStringPtr) { 248 | while (*aStringPtr != 0) { 249 | write1Start8Data1StopNoParityWithCliSei(*aStringPtr++); 250 | } 251 | } 252 | 253 | void writeStringSkipLeadingSpaces(const char *aStringPtr) { 254 | // skip leading spaces 255 | while (*aStringPtr == ' ' && *aStringPtr != 0) { 256 | aStringPtr++; 257 | } 258 | #if !defined(USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT) 259 | if (sUseCliSeiForWrite) { 260 | #endif 261 | while (*aStringPtr != 0) { 262 | write1Start8Data1StopNoParityWithCliSei(*aStringPtr++); 263 | } 264 | #if !defined(USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT) 265 | } else { 266 | while (*aStringPtr != 0) { 267 | write1Start8Data1StopNoParity(*aStringPtr++); 268 | } 269 | } 270 | #endif 271 | } 272 | 273 | void writeBinary(uint8_t aByte) { 274 | #if defined(USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT) 275 | write1Start8Data1StopNoParityWithCliSei(aByte); 276 | #else 277 | if (sUseCliSeiForWrite) { 278 | write1Start8Data1StopNoParityWithCliSei(aByte); 279 | } else { 280 | write1Start8Data1StopNoParity(aByte); 281 | } 282 | #endif 283 | } 284 | 285 | void writeChar(uint8_t aChar) { 286 | writeBinary(aChar); 287 | } 288 | 289 | void writeCRLF() { 290 | writeBinary('\r'); 291 | writeBinary('\n'); 292 | } 293 | 294 | void writeUnsignedByte(uint8_t aByte) { 295 | char tStringBuffer[4]; 296 | utoa(aByte, tStringBuffer, 10); 297 | writeStringSkipLeadingSpaces(tStringBuffer); 298 | } 299 | 300 | /* 301 | * 2 byte Hex output 302 | */ 303 | void writeUnsignedByteHex(uint8_t aByte) { 304 | char tStringBuffer[3]; 305 | // tStringBuffer[0] = nibbleToHex(aByte >> 4); 306 | // tStringBuffer[1] = nibbleToHex(aByte); 307 | // tStringBuffer[2] = '\0'; 308 | // the utoa() version is 8 bytes smaller than the version with nibbleToHex(), if utoa() is allocated by another function. 309 | utoa(aByte, &tStringBuffer[0], 16); 310 | if (tStringBuffer[1] == '\0') { 311 | tStringBuffer[2] = '\0'; 312 | tStringBuffer[1] = tStringBuffer[0]; 313 | tStringBuffer[0] = '0'; 314 | } 315 | writeString(tStringBuffer); 316 | } 317 | 318 | /* 319 | * 2 byte Hex output with 2 byte prefix "0x" 320 | */ 321 | void writeUnsignedByteHexWithPrefix(uint8_t aByte) { 322 | writeBinary('0'); 323 | writeBinary('x'); 324 | writeUnsignedByteHex(aByte); 325 | } 326 | 327 | char nibbleToHex(uint8_t aByte) { 328 | aByte = aByte & 0x0F; 329 | if (aByte < 10) { 330 | return aByte + '0'; 331 | } 332 | return aByte + 'A' - 10; 333 | } 334 | 335 | void writeByte(int8_t aByte) { 336 | char tStringBuffer[5]; 337 | itoa(aByte, tStringBuffer, 10); 338 | writeStringSkipLeadingSpaces(tStringBuffer); 339 | } 340 | 341 | void writeInt(int16_t aInteger) { 342 | char tStringBuffer[7]; 343 | itoa(aInteger, tStringBuffer, 10); 344 | writeStringSkipLeadingSpaces(tStringBuffer); 345 | } 346 | 347 | void writeUnsignedInt(uint16_t aInteger) { 348 | char tStringBuffer[6]; 349 | utoa(aInteger, tStringBuffer, 10); 350 | writeStringSkipLeadingSpaces(tStringBuffer); 351 | } 352 | 353 | void writeLong(int32_t aLong) { 354 | char tStringBuffer[12]; 355 | ltoa(aLong, tStringBuffer, 10); 356 | writeStringSkipLeadingSpaces(tStringBuffer); 357 | } 358 | 359 | void writeUnsignedLong(uint32_t aLong) { 360 | char tStringBuffer[11]; 361 | ultoa(aLong, tStringBuffer, 10); 362 | writeStringSkipLeadingSpaces(tStringBuffer); 363 | } 364 | 365 | void writeFloat(double aFloat) { 366 | char tStringBuffer[11]; 367 | dtostrf(aFloat, 10, 3, tStringBuffer); 368 | writeStringSkipLeadingSpaces(tStringBuffer); 369 | } 370 | 371 | void writeFloat(double aFloat, uint8_t aDigits) { 372 | char tStringBuffer[11]; 373 | dtostrf(aFloat, 10, aDigits, tStringBuffer); 374 | writeStringSkipLeadingSpaces(tStringBuffer); 375 | } 376 | 377 | /****************************************************** 378 | * The TinySerialOut class functions which implements 379 | * the Serial + printHex() and printlnHex() functions 380 | ******************************************************/ 381 | /* 382 | * An alternative way to call the init function :-) 383 | */ 384 | void TinySerialOut::begin(long aBaudrate) { 385 | initTXPin(); 386 | #if defined(_USE_115200BAUD) // else smaller code, but only 38400 baud at 1 MHz 387 | if (aBaudrate != 115200) { 388 | println(F("Only 115200 supported!")); 389 | } 390 | #else 391 | #if (F_CPU == 1000000) 392 | if (aBaudrate != 38400) { 393 | println(F("Only 38400 supported!")); 394 | } 395 | #else 396 | if (aBaudrate != 230400) { 397 | println(F("Only 230400 supported!")); 398 | } 399 | #endif 400 | #endif 401 | } 402 | 403 | void TinySerialOut::end() { 404 | // no action needed 405 | } 406 | 407 | void TinySerialOut::flush() { 408 | // no action needed, since we do not use a buffer 409 | } 410 | 411 | /* 412 | * 2 byte Hex output with 2 byte prefix "0x" 413 | */ 414 | void TinySerialOut::printHex(uint8_t aByte) { 415 | writeUnsignedByteHexWithPrefix(aByte); 416 | } 417 | 418 | void TinySerialOut::printHex(uint16_t aWord) { 419 | writeUnsignedByteHexWithPrefix(aWord >> 8); 420 | writeUnsignedByteHex(aWord); 421 | } 422 | 423 | void TinySerialOut::printlnHex(uint8_t aByte) { 424 | printHex(aByte); 425 | println(); 426 | } 427 | 428 | void TinySerialOut::printlnHex(uint16_t aWord) { 429 | printHex(aWord); 430 | println(); 431 | } 432 | 433 | // virtual functions of Print class 434 | size_t TinySerialOut::write(uint8_t aByte) { 435 | writeBinary(aByte); 436 | return 1; 437 | } 438 | #if !defined(TINY_SERIAL_INHERIT_FROM_PRINT) 439 | 440 | void TinySerialOut::print(const char *aStringPtr) { 441 | writeString(aStringPtr); 442 | } 443 | 444 | void TinySerialOut::print(const __FlashStringHelper *aStringPtr) { 445 | writeString(aStringPtr); 446 | } 447 | 448 | void TinySerialOut::print(char aChar) { 449 | writeBinary(aChar); 450 | } 451 | 452 | void TinySerialOut::print(uint8_t aByte, uint8_t aBase) { 453 | if (aBase == 16) { 454 | /* 455 | * Print Hex always with two characters 456 | */ 457 | writeUnsignedByteHex(aByte); 458 | } else { 459 | char tStringBuffer[4]; 460 | utoa(aByte, tStringBuffer, aBase); 461 | writeStringSkipLeadingSpaces(tStringBuffer); 462 | } 463 | } 464 | 465 | void TinySerialOut::print(int16_t aInteger, uint8_t aBase) { 466 | char tStringBuffer[7]; 467 | itoa(aInteger, tStringBuffer, aBase); 468 | writeStringSkipLeadingSpaces(tStringBuffer); 469 | } 470 | 471 | void TinySerialOut::print(uint16_t aInteger, uint8_t aBase) { 472 | char tStringBuffer[6]; 473 | utoa(aInteger, tStringBuffer, aBase); 474 | writeStringSkipLeadingSpaces(tStringBuffer); 475 | } 476 | 477 | void TinySerialOut::print(int32_t aLong, uint8_t aBase) { 478 | char tStringBuffer[12]; 479 | ltoa(aLong, tStringBuffer, aBase); 480 | writeStringSkipLeadingSpaces(tStringBuffer); 481 | } 482 | 483 | void TinySerialOut::print(uint32_t aLong, uint8_t aBase) { 484 | char tStringBuffer[11]; 485 | ultoa(aLong, tStringBuffer, aBase); 486 | writeStringSkipLeadingSpaces(tStringBuffer); 487 | } 488 | 489 | void TinySerialOut::print(double aFloat, uint8_t aDigits) { 490 | char tStringBuffer[11]; 491 | dtostrf(aFloat, 10, aDigits, tStringBuffer); 492 | writeStringSkipLeadingSpaces(tStringBuffer); 493 | } 494 | 495 | void TinySerialOut::println(char aChar) { 496 | print(aChar); 497 | println(); 498 | } 499 | 500 | void TinySerialOut::println(const char *aStringPtr) { 501 | print(aStringPtr); 502 | println(); 503 | } 504 | 505 | void TinySerialOut::println(const __FlashStringHelper *aStringPtr) { 506 | print(aStringPtr); 507 | println(); 508 | } 509 | 510 | void TinySerialOut::println(uint8_t aByte, uint8_t aBase) { 511 | print(aByte, aBase); 512 | println(); 513 | } 514 | 515 | void TinySerialOut::println(int16_t aInteger, uint8_t aBase) { 516 | print(aInteger, aBase); 517 | println(); 518 | } 519 | 520 | void TinySerialOut::println(uint16_t aInteger, uint8_t aBase) { 521 | print(aInteger, aBase); 522 | println(); 523 | } 524 | 525 | void TinySerialOut::println(int32_t aLong, uint8_t aBase) { 526 | print(aLong, aBase); 527 | println(); 528 | } 529 | 530 | void TinySerialOut::println(uint32_t aLong, uint8_t aBase) { 531 | print(aLong, aBase); 532 | println(); 533 | } 534 | 535 | void TinySerialOut::println(double aFloat, uint8_t aDigits) { 536 | print(aFloat, aDigits); 537 | println(); 538 | } 539 | 540 | void TinySerialOut::println() { 541 | print('\r'); 542 | print('\n'); 543 | } 544 | #endif // !defined(TINY_SERIAL_INHERIT_FROM_PRINT) 545 | 546 | /******************************** 547 | * Basic serial output function 548 | *******************************/ 549 | 550 | /* 551 | * Formula is only valid for constant values 552 | * Loading of constant value adds 2 extra cycles (check .lss file for exact timing) 553 | * 554 | * The loop takes 4 cycles (4 microseconds at 1 MHz). Last loop is only 3 cycles. 555 | * 1 -> 3(+2) cycles 556 | * 2 -> 7(+2) cycles 557 | * 3 -> 11(+2) cycles 558 | * 4 -> 15(+2) cycles 559 | * 5 -> 19(+2) cycles 560 | * 6 -> 23(+2) cycles 561 | */ 562 | inline void delay4CyclesExact(uint16_t a4Microseconds) { 563 | asm volatile ( 564 | "1: sbiw %0,1" "\n\t" // 2 cycles 565 | "brne .-4" : "=w" (a4Microseconds) : "0" (a4Microseconds)// 2 cycles 566 | ); 567 | } 568 | 569 | #if (F_CPU == 1000000) && defined(_USE_115200BAUD) // else around 120 bytes smaller code, but only 38400 baud at 1 MHz 570 | /* 571 | * 115200 baud - 8,680 cycles per bit, 86,8 per byte at 1 MHz 572 | * 573 | * Assembler code for 115200 baud extracted from Digispark core files: 574 | * Code size is 196 byte (including first call) 575 | * 576 | * TinySerialOut.h - Tiny write-only software serial. 577 | * Copyright 2010 Rowdy Dog Software. This code is part of Arduino-Tiny. 578 | * 579 | * Arduino-Tiny is free software: you can redistribute it and/or modify it 580 | * under the terms of the GNU Lesser General Public License as published by 581 | * the Free Software Foundation, either version 3 of the License, or (at your 582 | * option) any later version. 583 | */ 584 | void write1Start8Data1StopNoParity(uint8_t aValue) { 585 | asm volatile 586 | ( 587 | "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- 0 */ 588 | "ror %[value]" "\n\t" /* 1 */ 589 | "nop" "\n\t" /* 1 */ 590 | "nop" "\n\t" /* 1 */ 591 | "nop" "\n\t" /* 1 */ 592 | "nop" "\n\t" /* 1 */ 593 | 594 | "brcs L%=b0h" "\n\t" /* 1 (not taken) */ 595 | "nop" "\n\t" /* 1 */ 596 | "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- st is 9 cycles */ 597 | "rjmp L%=b0z" "\n\t" /* 2 */ 598 | "L%=b0h: " /* 2 (taken) */ 599 | "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- st is 9 cycles */ 600 | "nop" "\n\t" /* 1 */ 601 | "nop" "\n\t" /* 1 */ 602 | "L%=b0z: " 603 | "ror %[value]" "\n\t" /* 1 */ 604 | 605 | "nop" "\n\t" /* 1 */ 606 | 607 | "brcs L%=b1h" "\n\t" /* 1 (not taken) */ 608 | "nop" "\n\t" /* 1 */ 609 | "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- b0 is 8 cycles */ 610 | "rjmp L%=b1z" "\n\t" /* 2 */ 611 | "L%=b1h: " /* 2 (taken) */ 612 | "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b0 is 8 cycles */ 613 | "nop" "\n\t" /* 1 */ 614 | "nop" "\n\t" /* 1 */ 615 | "L%=b1z: " 616 | "ror %[value]" "\n\t" /* 1 */ 617 | 618 | "nop" "\n\t" /* 1 */ 619 | "nop" "\n\t" /* 1 */ 620 | 621 | "brcs L%=b2h" "\n\t" /* 1 (not taken) */ 622 | "nop" "\n\t" /* 1 */ 623 | "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- b1 is 9 cycles */ 624 | "rjmp L%=b2z" "\n\t" /* 2 */ 625 | "L%=b2h: " /* 2 (taken) */ 626 | "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b1 is 9 cycles */ 627 | "nop" "\n\t" /* 1 */ 628 | "nop" "\n\t" /* 1 */ 629 | "L%=b2z: " 630 | "ror %[value]" "\n\t" /* 1 */ 631 | 632 | "nop" "\n\t" /* 1 */ 633 | "nop" "\n\t" /* 1 */ 634 | 635 | "brcs L%=b3h" "\n\t" /* 1 (not taken) */ 636 | "nop" "\n\t" /* 1 */ 637 | "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- b2 is 9 cycles */ 638 | "rjmp L%=b3z" "\n\t" /* 2 */ 639 | "L%=b3h: " /* 2 (taken) */ 640 | "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b2 is 9 cycles */ 641 | "nop" "\n\t" /* 1 */ 642 | "nop" "\n\t" /* 1 */ 643 | "L%=b3z: " 644 | "ror %[value]" "\n\t" /* 1 */ 645 | 646 | "nop" "\n\t" /* 1 */ 647 | 648 | "brcs L%=b4h" "\n\t" /* 1 (not taken) */ 649 | "nop" "\n\t" /* 1 */ 650 | "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- b3 is 8 cycles */ 651 | "rjmp L%=b4z" "\n\t" /* 2 */ 652 | "L%=b4h: " /* 2 (taken) */ 653 | "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b3 is 8 cycles */ 654 | "nop" "\n\t" /* 1 */ 655 | "nop" "\n\t" /* 1 */ 656 | "L%=b4z: " 657 | "ror %[value]" "\n\t" /* 1 */ 658 | 659 | "nop" "\n\t" /* 1 */ 660 | "nop" "\n\t" /* 1 */ 661 | 662 | "brcs L%=b5h" "\n\t" /* 1 (not taken) */ 663 | "nop" "\n\t" /* 1 */ 664 | "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- b4 is 9 cycles */ 665 | "rjmp L%=b5z" "\n\t" /* 2 */ 666 | "L%=b5h: " /* 2 (taken) */ 667 | "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b4 is 9 cycles */ 668 | "nop" "\n\t" /* 1 */ 669 | "nop" "\n\t" /* 1 */ 670 | "L%=b5z: " 671 | "ror %[value]" "\n\t" /* 1 */ 672 | 673 | "nop" "\n\t" /* 1 */ 674 | "nop" "\n\t" /* 1 */ 675 | 676 | "brcs L%=b6h" "\n\t" /* 1 (not taken) */ 677 | "nop" "\n\t" /* 1 */ 678 | "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- b5 is 9 cycles */ 679 | "rjmp L%=b6z" "\n\t" /* 2 */ 680 | "L%=b6h: " /* 2 (taken) */ 681 | "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b5 is 9 cycles */ 682 | "nop" "\n\t" /* 1 */ 683 | "nop" "\n\t" /* 1 */ 684 | "L%=b6z: " 685 | "ror %[value]" "\n\t" /* 1 */ 686 | 687 | "nop" "\n\t" /* 1 */ 688 | 689 | "brcs L%=b7h" "\n\t" /* 1 (not taken) */ 690 | "nop" "\n\t" /* 1 */ 691 | "cbi %[txport], %[txpin]" "\n\t" /* 2 <--- b6 is 8 cycles */ 692 | "rjmp L%=b7z" "\n\t" /* 2 */ 693 | "L%=b7h: " /* 2 (taken) */ 694 | "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b6 is 8 cycles */ 695 | "nop" "\n\t" /* 1 */ 696 | "nop" "\n\t" /* 1 */ 697 | "L%=b7z: " 698 | "nop" "\n\t" /* 1 */ 699 | 700 | "nop" "\n\t" /* 1 */ 701 | "nop" "\n\t" /* 1 */ 702 | 703 | "nop" "\n\t" /* 1 */ 704 | "nop" "\n\t" /* 1 */ 705 | "sbi %[txport], %[txpin]" "\n\t" /* 2 <--- b7 is 9 cycles */ 706 | "nop" "\n\t" /* 1 */ 707 | "nop" "\n\t" /* 1 */ 708 | "nop" "\n\t" /* 1 */ 709 | "nop" "\n\t" /* 1 */ 710 | "nop" "\n\t" /* 1 */ 711 | "nop" "\n\t" /* 1 */ 712 | "nop" "\n\t" /* 1 */ 713 | /* <---sp is 9 cycles */ 714 | 715 | : 716 | : 717 | [value] "r" ( aValue ), 718 | [txport] "I" ( TX_PORT_ADDR ), 719 | [txpin] "I" ( TX_BIT_NUMBER ) 720 | ); 721 | } 722 | #else 723 | /* 724 | * Small code using loop. Code size is 76 byte (including first call) 725 | * 726 | * 1 MHz CPU Clock 727 | * 26,04 cycles per bit, 260,4 per byte for 38400 baud at 1 MHz Clock 728 | * 17,36 cycles per bit, 173,6 per byte for 57600 baud at 1 MHz Clock -> therefore use 38400 baud 729 | * 24 cycles between each cbi/sbi (Clear/Set Bit in IO-register) command. 730 | * 731 | * 8 MHz CPU Clock 732 | * 69,44 cycles per bit, 694,4 per byte for 115200 baud at 8 MHz Clock 733 | * 34,72 cycles per bit, 347,2 per byte for 230400 baud at 8 MHz Clock. 734 | * 68 / 33 cycles between each cbi (Clear Bit in IO-register) or sbi command. 735 | * 736 | * 16 MHz CPU Clock 737 | * 138,88 cycles per bit, 1388,8 per byte for 115200 baud at 16 MHz Clock 738 | * 69,44 cycles per bit, 694,4 per byte for 230400 baud at 16 MHz Clock 739 | * 137 / 68 cycles between each cbi (Clear Bit in IO-register) or sbi command. 740 | * 741 | * 2 cycles for each cbi/sbi instruction. 742 | */ 743 | void write1Start8Data1StopNoParity(uint8_t aValue) { 744 | asm volatile 745 | ( 746 | "cbi %[txport] , %[txpin]" "\n\t" // 2 PORTB &= ~(1 << TX_BIT_NUMBER); 747 | #if (F_CPU == 1000000) && !defined(_USE_115200BAUD) // 1 MHz 38400 baud 748 | // 0 cycles padding to get additional 4 cycles 749 | //delay4CyclesExact(5); -> 20 cycles 750 | "ldi r30 , 0x05" "\n\t"// 1 751 | #elif ((F_CPU == 8000000) && defined(_USE_115200BAUD)) || ((F_CPU == 16000000) && !defined(_USE_115200BAUD)) // 8 MHz 115200 baud OR 16 MHz 230400 baud 752 | // 3 cycles padding to get additional 7 cycles 753 | "nop" "\n\t"// 1 _nop"(); 754 | "nop" "\n\t"// 1 _nop"(); 755 | "nop" "\n\t"// 1 _nop"(); 756 | //delay4CyclesExact(15); -> 61 cycles 757 | "ldi r30 , 0x0F" "\n\t"// 1 758 | #elif (F_CPU == 8000000) && !defined(_USE_115200BAUD) // 8 MHz 230400 baud 759 | // 0 cycles padding to get additional 4 cycles 760 | //delay4CyclesExact(7); -> 29 cycles 761 | "ldi r30 , 0x07" "\n\t"// 1 762 | #elif (F_CPU == 16000000) && defined(_USE_115200BAUD) // 16 MHz 115200 baud 763 | // 0 cycles padding to get additional 4 cycles 764 | //delay4CyclesExact(33); -> 133 cycles 765 | "ldi r30 , 0x21" "\n\t"// 1 766 | #endif 767 | "ldi r31 , 0x00" "\n\t" // 1 768 | "delay1:" 769 | "sbiw r30 , 0x01" "\n\t"// 2 770 | "brne delay1" "\n\t"// 1-2 771 | 772 | "ldi r25 , 0x08" "\n\t"// 1 773 | 774 | // Start of loop 775 | // if (aValue & 0x01) { 776 | "txloop:" 777 | "sbrs %[value] , 0" "\n\t"// 1 778 | "rjmp .+6" "\n\t"// 2 779 | 780 | "nop" "\n\t"// 1 781 | "sbi %[txport] , %[txpin]" "\n\t"// 2 PORTB |= 1 << TX_BIT_NUMBER; 782 | "rjmp .+6" "\n\t"// 2 783 | 784 | "cbi %[txport] , %[txpin]" "\n\t"// 2 PORTB &= ~(1 << TX_BIT_NUMBER); 785 | "nop" "\n\t"// 1 786 | "nop" "\n\t"// 1 787 | "lsr %[value]" "\n\t"// 1 aValue = aValue >> 1; 788 | 789 | #if (F_CPU == 1000000) && !defined(_USE_115200BAUD) // 1 MHz 38400 baud 790 | // 3 cycles padding to get additional 11 cycles 791 | "nop" "\n\t"// 1 792 | "nop" "\n\t"// 1 793 | "nop" "\n\t"// 1 794 | // delay4CyclesExact(3); -> 13 cycles 795 | "ldi r30 , 0x03" "\n\t"// 1 796 | #elif ((F_CPU == 8000000) && defined(_USE_115200BAUD)) || ((F_CPU == 16000000) && !defined(_USE_115200BAUD)) // 8 MHz 115200 baud OR 16 MHz 230400 baud 797 | // 3 cycles padding to get additional 11 cycles 798 | "nop" "\n\t"// 1 799 | "nop" "\n\t"// 1 800 | "nop" "\n\t"// 1 801 | // delay4CyclesExact(14); -> 57 cycles 802 | "ldi r30 , 0x0E" "\n\t"// 1 803 | #elif (F_CPU == 8000000) && !defined(_USE_115200BAUD) // 8 MHz 230400 baud 804 | // 0 cycles padding to get additional 8 cycles 805 | // delay4CyclesExact(6); -> 25 cycles 806 | "ldi r30 , 0x05" "\n\t"// 1 807 | #elif (F_CPU == 16000000) && defined(_USE_115200BAUD) // 16 MHz 115200 baud 808 | // 0 cycles padding to get additional 8 cycles 809 | //delay4CyclesExact(32); -> 129 cycles 810 | "ldi r30 , 0x20" "\n\t"// 1 811 | #endif 812 | "ldi r31 , 0x00" "\n\t" // 1 813 | "delay2:" 814 | "sbiw r30 , 0x01" "\n\t"// 2 815 | "brne delay2" "\n\t"// 1-2 816 | 817 | // }while (i > 0); 818 | "subi r25 , 0x01" "\n\t"// 1 819 | "brne txloop" "\n\t"// 1-2 820 | // To compensate for missing loop cycles at last bit 821 | "nop" "\n\t"// 1 822 | "nop" "\n\t"// 1 823 | "nop" "\n\t"// 1 824 | "nop" "\n\t"// 1 825 | 826 | // Stop bit 827 | "sbi %[txport] , %[txpin]" "\n\t"// 2 PORTB |= 1 << TX_BIT_NUMBER; 828 | 829 | #if (F_CPU == 1000000) && !defined(_USE_115200BAUD) // 1 MHz 38400 baud 830 | // delay4CyclesExact(4); -> 17 cycles - gives minimum 25 cycles for stop bit 831 | "ldi r30 , 0x04" "\n\t"// 1 832 | #elif ((F_CPU == 8000000) && defined(_USE_115200BAUD)) || ((F_CPU == 16000000) && !defined(_USE_115200BAUD)) // 8 MHz 115200 baud OR 16 MHz 230400 baud 833 | // delay4CyclesExact(15) -> 61 cycles - gives minimum 69 cycles for stop bit 834 | "ldi r30 , 0x0F" "\n\t"// 1 835 | #elif (F_CPU == 8000000) && !defined(_USE_115200BAUD) // 8 MHz 230400 baud 836 | // delay4CyclesExact(5) -> 27 cycles - gives minimum 35 cycles for stop bit 837 | "ldi r30 , 0x05" "\n\t"// 1 838 | #elif (F_CPU == 16000000) && defined(_USE_115200BAUD) // 16 MHz 115200 baud 839 | // delay4CyclesExact(32) -> 129 cycles - gives minimum 137 cycles for stop bit 840 | "ldi r30 , 0x20" "\n\t"// 1 841 | #endif 842 | "ldi r31 , 0x00" "\n\t" // 1 843 | "delay3:" 844 | "sbiw r30 , 0x01" "\n\t"// 845 | "brne delay3" "\n\t"// 1-2 846 | // return needs 4 cycles, load of next value needs 1 cycle, next rcall needs 3 cycles -> gives additional 8 cycles minimum for stop bit 847 | 848 | : 849 | : 850 | [value] "r" ( aValue ), 851 | [txport] "I" ( TX_PORT_ADDR ) , /* 0x18 is PORTB on Attiny 85 */ 852 | [txpin] "I" ( TX_BIT_NUMBER ) 853 | : 854 | "r25", 855 | "r30", 856 | "r31" 857 | ); 858 | 859 | } 860 | #endif 861 | 862 | /* 863 | * C Version which generates the assembler code above. 864 | * In order to guarantee the correct timing, compile with Arduino standard settings or: 865 | * avr-g++ -I"C:\arduino\hardware\arduino\avr\cores\arduino" -I"C:\arduino\hardware\arduino\avr\variants\standard" -c -g -w -Os -ffunction-sections -fdata-sections -mmcu=attiny85 -DF_CPU=1000000UL -MMD -o "TinySerialOut.o" "TinySerialOut.cpp" 866 | * Tested with Arduino 1.6.8 and 1.8.5/gcc4.9.2 867 | * C Version does not work with AVR gcc7.3.0, since optimization is too bad 868 | */ 869 | void write1Start8Data1StopNoParity_C_Version(uint8_t aValue) { 870 | /* 871 | * C Version here for 38400 baud at 1 MHz Clock. You see, it is simple :-) 872 | */ 873 | // start bit 874 | TX_PORT &= ~(1 << TX_BIT_NUMBER); 875 | _NOP(); 876 | delay4CyclesExact(4); 877 | 878 | // 8 data bits 879 | uint8_t i = 8; 880 | do { 881 | if (aValue & 0x01) { 882 | // bit=1 883 | // to compensate for jump at data=0 884 | _NOP(); 885 | TX_PORT |= 1 << TX_BIT_NUMBER; 886 | } else { 887 | // bit=0 888 | TX_PORT &= ~(1 << TX_BIT_NUMBER); 889 | // compensate for different cycles of sbrs 890 | _NOP(); 891 | _NOP(); 892 | } 893 | aValue = aValue >> 1; 894 | // 3 cycles padding 895 | _NOP(); 896 | _NOP(); 897 | _NOP(); 898 | delay4CyclesExact(3); 899 | --i; 900 | } while (i > 0); 901 | 902 | // to compensate for missing loop cycles at last bit 903 | _NOP(); 904 | _NOP(); 905 | _NOP(); 906 | _NOP(); 907 | 908 | // Stop bit 909 | TX_PORT |= 1 << TX_BIT_NUMBER; 910 | // -8 cycles to compensate for fastest repeated call (1 ret + 1 load + 1 call) 911 | delay4CyclesExact(4); // gives minimum 25 cycles for stop bit :-) 912 | } 913 | #elif defined(ARDUINO_ARCH_APOLLO3) 914 | void AttinySerialOutDummyToAvoidBFDAssertions(){ 915 | ; 916 | } 917 | #endif // defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__) 918 | 919 | #endif // _ATTINY_SERIAL_OUT_HPP 920 | -------------------------------------------------------------------------------- /OpenWindowAlarm/OpenWindowAlarm.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * OpenWindowAlarm.cpp 3 | * This program must run at 1MHz, select Digispark (1mhz � No USB) or equivalent as your board! 4 | * 5 | * Overview: 6 | * Every 24 seconds a sample is taken of the ATtiny internal temperature sensor which has a resolution of 1 degree. 7 | * If temperature is lower than "old" temperature value, then an alarm is issued 5 minutes later, if "the condition still holds". 8 | * Detection of an open window is indicated by a longer 20 ms blink and a short click every 24 seconds. 9 | * A low battery (below 3.55 volt for LiPo) is indicated by beeping and flashing LED every 24 seconds. Only the beep (not the flash) is significantly longer than at open window detection. 10 | * 11 | * Internal operation: 12 | * An open window is detected after `TEMPERATURE_COMPARE_AMOUNT * TEMPERATURE_SAMPLE_SECONDS` (48) seconds of reading a temperature with a value of `TEMPERATURE_DELTA_THRESHOLD_DEGREE` (2) lower 13 | than the temperature `TEMPERATURE_COMPARE_DISTANCE * TEMPERATURE_SAMPLE_SECONDS` (192 seconds-> 3 minutes and 12 seconds) before. 14 | * The delay is implemented by sleeping 3 times at `SLEEP_MODE_PWR_DOWN` for a period of 8 seconds -the maximum hardware sleep time- to reduce power consumption. 15 | * If an **open window is detected**, this is indicated by a longer **20 ms blink** and a **short click** every 24 seconds. 16 | Therefore, the internal sensor has a time of 3 minutes to adjust to the outer temperature in order to capture even small changes in temperature. 17 | The greater the temperature change the earlier the sensor value will change and detect an open window. 18 | * `OPEN_WINDOW_ALARM_DELAY_MINUTES` (5) minutes after open window detection the **alarm is activated**.
19 | The alarm will not start or an activated alarm will stop if the current temperature is greater than the minimum measured temperature (+ 1) i.e. the window has been closed already. 20 | * The **initial alarm** lasts for 10 minutes. After this, it is activated for a period of 10 seconds with a increasing break from 24 seconds up to 5 minutes. 21 | * At **power-on** the VCC voltage is measured used to **determine the type of battery** using `VCC_VOLTAGE_LIPO_DETECTION` (3.6 volt). 22 | * Every `VCC_MONITORING_DELAY_MIN` (60) minutes the battery voltage is measured. Depending on the detected battery type, **low battery voltage** is indicated by **beeping and flashing the LED every 24 seconds**. 23 | Only the beep (not the flash) is significantly longer than the beep for an open window detection.
24 | Low battery voltage is defined by `VCC_VOLTAGE_LOWER_LIMIT_MILLIVOLT_LIPO` (3550 millivolt) or `VCC_VOLTAGE_LOWER_LIMIT_MILLIVOLT_STANDARD` (2350 Millivolt). 25 | * After power-on, the **inactive settling time** is 5 minutes. If the board is getting colder during the settling time, 4:15 (or 8:30) minutes are added to avoid false alarms after power-on. 26 | 27 | * If you enable `DEBUG` by commenting out line 60, you can monitor the serial output with 115200 baud at P2 to see what is happening. 28 | * 29 | * Power consumption: 30 | * Power consumption is 26 uA at sleep and 2.8 mA at at 1 MHz active. 31 | * The software loop needs 2.1 ms and with DEBUG 6.5 ms (plus 3 times 1 ms startup time) => active time is around 1/5000 or 1/2500 of total time. 32 | * During the loop the power consumption is 100 times more than sleep => Loop adds only 2% to 4% to total power consumption. 33 | * If you reprogram the fuses, you can get 6 uA power consumption. 34 | * For the 6 uA scenario, loop current is 500 times and startup time is negligible => loop adds 5% to 12% to total (lower) power consumption. 35 | * 36 | * 37 | * Copyright (C) 2018-2019 Armin Joachimsmeyer 38 | * armin.joachimsmeyer@gmail.com 39 | * 40 | * This file is part of Arduino-OpenWindowAlarm https://github.com/ArminJo/Arduino-OpenWindowAlarm. 41 | * 42 | * Arduino-OpenWindowAlarm is free software: you can redistribute it and/or modify 43 | * it under the terms of the GNU General Public License as published by 44 | * the Free Software Foundation, either version 3 of the License, or 45 | * (at your option) any later version. 46 | * 47 | * This program is distributed in the hope that it will be useful, 48 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 49 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 50 | * See the GNU General Public License for more details. 51 | * 52 | * You should have received a copy of the GNU General Public License 53 | * along with this program. If not, see . 54 | * 55 | */ 56 | 57 | #include 58 | 59 | /* 60 | * To see serial output, you must select "Digispark (1mhz � No USB)" as Board in the Arduino IDE! 61 | * And you need TinySerialOut.h + TinySerialOut.cpp in your sketch folder. 62 | */ 63 | //#define DEBUG // To see serial output with 115200 baud at P2 - 64 | //#define TRACE // To see more serial output at startup with 115200 baud at P2 65 | #define ALARM_TEST // start alarm immediately if PB0 / P0 is connected to ground 66 | 67 | #if defined(TRACE) 68 | #define DEBUG 69 | #endif 70 | #if defined(DEBUG) 71 | #include "ATtinySerialOut.hpp" // Available as Arduino library "ATtinySerialOut" 72 | #endif 73 | 74 | #include // required for boot_signature_byte_get() 75 | #include // required for clock_prescale_set() 76 | #include // required for sleep_enable() 77 | #include // required for WDTO_8S 78 | 79 | #define VERSION "1.3.3" 80 | /* 81 | * Version 1.3.3 82 | * - 3. party libs. 83 | * Version 1.3.2 84 | * - Adapted MCUSR handling. 85 | * Version 1.3.1 86 | * - Check for closed window happens only the first 10 minutes of alarm. 87 | * Version 1.3.0 88 | * - Changed voltage low detection. 89 | * - Improved DEBUG output. 90 | * Version 1.2.2 91 | * - Converted to Serial.print. 92 | * - New PWMTone() without tone(). 93 | * Version 1.2.1 94 | * - Fixed bug in check for temperature rising after each alarm. 95 | * Version 1.2 96 | * - Improved sleep, detecting closed window also after start of alarm, reset behavior. 97 | * - Changed LIPO detection threshold. 98 | * - Fixed analog reference bug. 99 | */ 100 | 101 | #if defined(ALARM_TEST) 102 | #define ALARM_TEST_PIN PB0 103 | #endif 104 | 105 | #define OPEN_WINDOW_ALARM_DELAY_MINUTES 5 // Wait time between window open detection and activation of alarm 106 | const int OPEN_WINDOW_ALARM_FREQUENCY_HIGH = 2200; // Should be the resonance frequency of speaker/buzzer 107 | const int OPEN_WINDOW_ALARM_FREQUENCY_LOW = 1100; 108 | const int OPEN_WINDOW_ALARM_FREQUENCY_VCC_TOO_LOW = 1600; // Use a different frequency to distinguish the this alert from others 109 | 110 | /* 111 | * Temperature timing 112 | */ 113 | const uint16_t TEMPERATURE_SAMPLE_SECONDS = 24; // Use multiple of 8 here 114 | const uint8_t OPEN_WINDOW_SAMPLES = (OPEN_WINDOW_ALARM_DELAY_MINUTES * 60) / TEMPERATURE_SAMPLE_SECONDS; 115 | const uint8_t TEMPERATURE_COMPARE_AMOUNT = 2; // compare 2 values 116 | const uint8_t TEMPERATURE_COMPARE_DISTANCE = 8; // 3 minutes and 12 seconds 117 | // 2. compare for slower decreasing temperatures 118 | const uint8_t TEMPERATURE_COMPARE_2_DISTANCE = (4 * TEMPERATURE_COMPARE_DISTANCE); // 12 minutes and 48 seconds 119 | 120 | // Array to hold enough values to compare TEMPERATURE_COMPARE_AMOUNT values with the same amount of values TEMPERATURE_COMPARE_DISTANCE positions before 121 | const uint8_t TEMPERATURE_ARRAY_SIZE = TEMPERATURE_COMPARE_AMOUNT + TEMPERATURE_COMPARE_DISTANCE + TEMPERATURE_COMPARE_AMOUNT; 122 | uint16_t sTemperatureArray[TEMPERATURE_ARRAY_SIZE]; // value at 0 is newest one 123 | 124 | /* 125 | * Temperature values 126 | */ 127 | const uint16_t TEMPERATURE_DELTA_THRESHOLD_DEGREE = 2; // 1 LSB = 1 Degree Celsius 128 | const uint16_t TEMPERATURE_DELTA_2_THRESHOLD_DEGREE = (2 * TEMPERATURE_DELTA_THRESHOLD_DEGREE); // 1 LSB = 1 Degree Celsius 129 | uint16_t sTemperatureNewSum = 0; 130 | uint16_t sTemperatureOldSum = 0; 131 | 132 | uint16_t sTemperatureMinimumAfterWindowOpen; // for window close detection 133 | uint16_t sTemperatureAtWindowOpen; 134 | 135 | /* 136 | * Detection flags 137 | */ 138 | bool sOpenWindowDetected = false; 139 | bool sOpenWindowDetectedOld = false; 140 | uint8_t sOpenWindowSampleDelayCounter; 141 | 142 | /* 143 | * VCC monitoring 144 | */ 145 | const uint16_t VCC_VOLTAGE_USB_DETECTION = 4300; // Above 4.3 volt we assume that USB is attached. 146 | const uint16_t VCC_VOLTAGE_LOWER_LIMIT_MILLIVOLT_LIPO = 3550; // 3.7 volt is the normal operating voltage if powered by a LiPo battery 147 | const uint16_t VCC_VOLTAGE_LIPO_DETECTION = 3450; // Above 3.45 volt we assume that a LIPO battery is attached, below we assume a CR2032 or two AA or AAA batteries are attached. 148 | const uint16_t VCC_VOLTAGE_LOWER_LIMIT_MILLIVOLT_STANDARD = 2600; // 3.0 volt is normal operating voltage if powered by a CR2032 or two AA or AAA batteries. 149 | const uint16_t VCC_VOLTAGE_LOWER_LIMIT_MILLIVOLT_STANDARD_BOD2_7 = 2800; // BOD is at 2.7 volt typically, so we cannot get values below that 150 | 151 | uint16_t sVCCVoltageMillivolt; 152 | bool sVCCVoltageTooLow; 153 | bool sLIPOSupplyDetected; 154 | bool sBODLevelIsBelow2_7; 155 | uint8_t getBODLevelFuses(); 156 | bool isBODSFlagExistent(); 157 | void checkVCCPeriodically(); 158 | 159 | const uint8_t VCC_MONITORING_DELAY_MIN = 60; // Check VCC every hour, because this costs extra power. 160 | uint16_t sVCCMonitoringDelayCounter; // Counter for VCC monitoring. 161 | 162 | // 163 | // ATMEL ATTINY85 164 | // 165 | // +-\/-+ 166 | // RESET/ADC0 (D5) PB5 1| |8 Vcc 167 | // Tone USB+ ADC3 (D3) PB3 2| |7 PB2 (D2) INT0/ADC1 - TX Debug output 168 | // Tone inv. USB- ADC2 (D4) PB4 3| |6 PB1 (D1) MISO/DO/AIN1/OC0B/OC1A/PCINT1 - (Digispark) LED 169 | // GND 4| |5 PB0 (D0) OC0A/AIN0 - Alarm Test if connected to ground 170 | // +----+ 171 | 172 | #define LED_PIN PB1 173 | #define TONE_PIN PB4 174 | #define TONE_PIN_INVERTED PB3 175 | 176 | #define ADC_TEMPERATURE_CHANNEL_MUX 15 177 | #define ADC_1_1_VOLT_CHANNEL_MUX 12 178 | // 0 1 0 Internal 1.1 volt voltage Reference. 179 | #define INTERNAL (2) 180 | #define INTERNAL1V1 INTERNAL 181 | #define SHIFT_VALUE_FOR_REFERENCE REFS0 182 | 183 | #if (LED_PIN == TX_PIN) 184 | #error LED pin must not be equal TX pin. 185 | #endif 186 | 187 | #define LED_PULSE_LENGTH 200 // 500 is well visible, 200 is OK 188 | #if (LED_PULSE_LENGTH < 150) 189 | #error LED_PULSE_LENGTH must at least be 150, since the code after digitalWrite(LED_PIN, 1) needs 150 us. 190 | #endif 191 | 192 | uint8_t sMCUSRStored; // content of MCUSR register at startup 193 | 194 | void PWMtone(unsigned int aFrequency, unsigned int aDurationMillis = 0); 195 | void delayAndSignalOpenWindowDetectionAndLowVCC(); 196 | void alarm(); 197 | void playDoubleClick(); 198 | void readTempAndManageHistory(); 199 | void resetHistory(); 200 | void initPeriodicSleepWithWatchdog(uint8_t tSleepMode, uint8_t aWatchdogPrescaler); 201 | void sleepDelay(uint16_t aSecondsToSleep); 202 | void delayMilliseconds(unsigned int aMillis); 203 | uint16_t readADCChannelWithReferenceOversample(uint8_t aChannelNumber, uint8_t aReference, uint8_t aOversampleExponent); 204 | uint16_t getVCCVoltageMillivolt(void); 205 | 206 | #if defined(DEBUG) 207 | void printFuses(void); 208 | void printBODSFlagExistence(); 209 | #endif 210 | 211 | #define STR_HELPER(x) #x 212 | #define STR(x) STR_HELPER(x) 213 | 214 | /*********************************************************************************** 215 | * Code starts here 216 | ***********************************************************************************/ 217 | 218 | void setup() { 219 | /* 220 | * store MCUSR early for later use 221 | */ 222 | if (MCUSR != 0) { 223 | sMCUSRStored = MCUSR; // content of MCUSR register at startup 224 | MCUSR = 0; // to prepare for next boot. 225 | } else { 226 | sMCUSRStored = GPIOR0; // Micronucleus puts a copy here 227 | GPIOR0 = 0; // Clear it to detect a jmp 0 228 | } 229 | 230 | #if defined(DEBUG) 231 | /* 232 | * Initialize the serial pin as an output for Serial.print like debugging 233 | */ 234 | initTXPin(); 235 | #endif 236 | 237 | /* 238 | * initialize the pins 239 | */ 240 | pinMode(LED_PIN, OUTPUT); 241 | pinMode(TONE_PIN_INVERTED, OUTPUT); 242 | pinMode(TONE_PIN, OUTPUT); 243 | #if defined(ALARM_TEST) 244 | pinMode(ALARM_TEST_PIN, INPUT_PULLUP); 245 | #endif 246 | 247 | sBODLevelIsBelow2_7 = (getBODLevelFuses() >= 6); 248 | 249 | #if defined(DEBUG) 250 | Serial.println(F("START " __FILE__ "\nVersion " VERSION " from " __DATE__ "\nAlarm delay = " STR(OPEN_WINDOW_ALARM_DELAY_MINUTES) " minutes")); 251 | 252 | Serial.print(F("Brown Out Detection is ")); 253 | if (getBODLevelFuses() == 7) { 254 | Serial.print(F("disabled => 6")); 255 | } else { 256 | Serial.print(F("enabled => ")); 257 | if (isBODSFlagExistent()) { 258 | // Can disable BOD for sleep 259 | Serial.print(F("6")); 260 | } else { 261 | Serial.print(F("25")); 262 | } 263 | } 264 | Serial.println(F(" micro Ampere sleep current")); 265 | #endif 266 | 267 | #if defined(TRACE) 268 | Serial.print(F("MCUSR=0x")); 269 | Serial.println(sMCUSRStored, HEX); 270 | Serial.print(F("WDTCR=0x")); 271 | Serial.println(WDTCR, HEX); 272 | Serial.print(F("OSCCAL=0x")); 273 | Serial.println(OSCCAL, HEX); 274 | printBODSFlagExistence(); 275 | printFuses(); 276 | #endif 277 | 278 | /* 279 | * init sleep mode and wakeup period 280 | */ 281 | initPeriodicSleepWithWatchdog(SLEEP_MODE_PWR_DOWN, WDTO_8S); 282 | 283 | /* 284 | * Initialize ADC channel and reference 285 | */ 286 | readADCChannelWithReferenceOversample(ADC_TEMPERATURE_CHANNEL_MUX, INTERNAL1V1, 0); 287 | 288 | /* 289 | * Signal power on with a single tone or signal reset with a double click. 290 | */ 291 | #if defined(DEBUG) 292 | Serial.print(F("Booting from ")); 293 | #endif 294 | if (sMCUSRStored & _BV(PORF)) { 295 | PWMtone(OPEN_WINDOW_ALARM_FREQUENCY_HIGH, 100); 296 | #if defined(DEBUG) 297 | Serial.println(F("power up")); 298 | #endif 299 | } else { 300 | playDoubleClick(); 301 | #if defined(DEBUG) 302 | Serial.println(F("reset")); 303 | #endif 304 | } 305 | 306 | /* 307 | * Blink LED at startup to show OPEN_WINDOW_MINUTES 308 | */ 309 | delayMilliseconds(1000); // wait extra second after bootloader blink 310 | for (int i = 0; i < OPEN_WINDOW_ALARM_DELAY_MINUTES; ++i) { 311 | // activate LED 312 | digitalWrite(LED_PIN, 1); 313 | delayMilliseconds(200); // delay() is disabled, so use delayMicroseconds() 314 | // deactivate LED 315 | digitalWrite(LED_PIN, 0); 316 | delayMilliseconds(200); 317 | } 318 | 319 | #if defined(ALARM_TEST) 320 | if (!digitalRead(ALARM_TEST_PIN)) { 321 | #if defined(DEBUG) 322 | Serial.println(F("Test signal out")); 323 | #endif 324 | alarm(); 325 | } 326 | #endif 327 | 328 | /* 329 | * Check VCC and decide if LIPO or 2 standard batteries / 1 button cell attached 330 | */ 331 | sVCCVoltageMillivolt = getVCCVoltageMillivolt(); 332 | if (sVCCVoltageMillivolt > VCC_VOLTAGE_LIPO_DETECTION) { 333 | sLIPOSupplyDetected = true; 334 | } else { 335 | sLIPOSupplyDetected = false; 336 | } 337 | 338 | sVCCMonitoringDelayCounter = 1; // 1 -> check directly now 339 | checkVCCPeriodically(); 340 | 341 | // disable digital input buffer to save power 342 | // do not disable buffer for outputs whose values are read back 343 | DIDR0 = _BV(ADC1D) | _BV(ADC2D) | _BV(ADC3D) | _BV(AIN1D) | _BV(AIN0D); 344 | 345 | // This disables Arduino delay() and millis() timer 0 and also its interrupts which kills the deep sleep. 346 | PRR = _BV(PRTIM0) | _BV(PRUSI); // Disable timer 0 and USI - has no effect on Power Down current 347 | 348 | /* 349 | * wait 8 seconds, since ATtinys temperature is increased after the micronucleus boot process 350 | * We do not disable ADC here, so we consume 212 uA 351 | */ 352 | sleep_cpu() 353 | ; 354 | } 355 | 356 | /* 357 | * Shift temperature history values ad insert new value. 358 | * Check if temperature decreases after power on. 359 | * Check if window was just opened. 360 | * If window was opened check if window still open -> ALARM 361 | * Loop needs 2.1 ms and with DEBUG 6.5 ms => active time is ca. 1/10k or 1/4k of total time and power consumption is 500 times more than sleep. 362 | * The sleep wakeup time for PLL clock 363 | * 2 ms for temperature reading 364 | * 0.25 ms for processing 365 | * 0.05 ms for LED flashing 366 | * + 4.4 ms for DEBUG 367 | */ 368 | void loop() { 369 | 370 | readTempAndManageHistory(); // needs 2 milliseconds 371 | 372 | // activate LED after reading to signal it. Do it here to reduce delay below. 373 | digitalWrite(LED_PIN, 1); 374 | 375 | /* 376 | * Check if we are just after boot and temperature is decreasing 377 | */ 378 | if ((sTemperatureArray[TEMPERATURE_ARRAY_SIZE - 1] == 0) && (sTemperatureArray[TEMPERATURE_ARRAY_SIZE - 2] > 0) 379 | /* 380 | * array is almost full, so check if temperature is lower than at boot time which means, 381 | * we ported the sensor from a warm place to its final one 382 | * or the window is still open and the user has pushed the reset button to avoid an alarm. 383 | */ 384 | && (sTemperatureArray[0] < sTemperatureArray[TEMPERATURE_ARRAY_SIZE - 2])) { 385 | // Start from beginning, clear temperature array 386 | #if defined(DEBUG) 387 | Serial.println(F("Detected porting to a colder place -> reset")); 388 | #endif 389 | resetHistory(); 390 | } else { 391 | 392 | if (!sOpenWindowDetected) { 393 | /* 394 | * Check if window just opened 395 | */ 396 | // tTemperatureOldSum can be 0 -> do not use tTemperatureNewSum < tTemperatureOldSum - (TEMPERATURE_DELTA_THRESHOLD_DEGREE * TEMPERATURE_COMPARE_AMOUNT) 397 | if (sTemperatureNewSum + (TEMPERATURE_DELTA_THRESHOLD_DEGREE * TEMPERATURE_COMPARE_AMOUNT) < sTemperatureOldSum) { 398 | #if defined(DEBUG) 399 | Serial.println(F("Detected window just opened -> check again in " STR(OPEN_WINDOW_ALARM_DELAY_MINUTES) " minutes")); 400 | #endif 401 | sTemperatureMinimumAfterWindowOpen = sTemperatureNewSum; 402 | sTemperatureAtWindowOpen = sTemperatureNewSum; 403 | sOpenWindowDetected = true; 404 | sOpenWindowSampleDelayCounter = 0; 405 | } 406 | } else { 407 | /* 408 | * Here open window is detected 409 | * First check if window already closed -> start a new detection 410 | */ 411 | if (sTemperatureNewSum > (sTemperatureMinimumAfterWindowOpen + TEMPERATURE_COMPARE_AMOUNT)) { 412 | sOpenWindowDetected = false; 413 | #if defined(DEBUG) 414 | Serial.println(F("Detected window already closed -> start again")); 415 | #endif 416 | // reset history in order to avoid a new detection at next sample, since tTemperatureNewSum may still be lower than tTemperatureOldSum 417 | resetHistory(); 418 | } else { 419 | if (sTemperatureNewSum < sTemperatureMinimumAfterWindowOpen) { 420 | // set new minimum temperature 421 | sTemperatureMinimumAfterWindowOpen = sTemperatureNewSum; 422 | } 423 | 424 | /* 425 | * Check if alarm delay was reached 426 | */ 427 | sOpenWindowSampleDelayCounter++; 428 | if (sOpenWindowSampleDelayCounter >= OPEN_WINDOW_SAMPLES) { 429 | /* 430 | * Here delay is reached 431 | * Check if still open - current temperature must be 1 degree lower than temperature at time of open detection 432 | * "- TEMPERATURE_COMPARE_AMOUNT": this reduces the sensibility, but helps to detect already closed windows. 433 | * You may remove this to increase sensibility. 434 | */ 435 | if (sTemperatureNewSum <= sTemperatureAtWindowOpen - TEMPERATURE_COMPARE_AMOUNT) { 436 | /* 437 | * Window is still open -> ALARM 438 | */ 439 | #if defined(DEBUG) 440 | Serial.println(F("Detected window still open -> alarm")); 441 | #endif 442 | alarm(); 443 | } else { 444 | // Temperature not 1 degree lower than temperature at time of open detection 445 | sOpenWindowDetected = false; 446 | #if defined(DEBUG) 447 | Serial.println(F("Assume wrong window open detection -> start again")); 448 | #endif 449 | } 450 | } // delay 451 | } // already closed 452 | } // !sOpenWindowDetected 453 | } // after power on and temperature is decreasing 454 | 455 | /* 456 | * VCC check every hour 457 | */ 458 | checkVCCPeriodically(); // needs 4.5 ms 459 | 460 | delayAndSignalOpenWindowDetectionAndLowVCC(); // Introduce a delay of 22 ms if open window is detected to let the LED light longer 461 | // deactivate LED before sleeping 462 | digitalWrite(LED_PIN, 0); 463 | 464 | sleepDelay(TEMPERATURE_SAMPLE_SECONDS); 465 | } 466 | 467 | /* 468 | * Like tone(), but use OC1B (PB4) and (inverted) !OC1B (PB3) 469 | */ 470 | void PWMtone(unsigned int aFrequency, unsigned int aDurationMillis) { 471 | 472 | // Determine which prescaler to use, we are running with 1 MHz now 473 | uint8_t tPrescaler = 0x01; 474 | uint16_t tOCR = 1000000 / aFrequency; // cannot use F_CPU since clock is set to 1 MHz at runtime 475 | while (tOCR > 0x100 && tPrescaler < 0x0F) { 476 | tPrescaler++; 477 | tOCR >>= 1; 478 | 479 | } 480 | 481 | OCR1C = tOCR - 1; // The frequency of the PWM will be Timer Clock 1 Frequency divided by (OCR1C value + 1). 482 | OCR1B = OCR1C / 2; // set PWM to 50% 483 | GTCCR = _BV(PWM1B) | _BV(COM1B0); // Switch to PWM Mode with OC1B (PB4) + !OC1B (PB3) outputs enabled 484 | TCCR1 = (tPrescaler << CS10); 485 | 486 | delayMilliseconds(aDurationMillis); 487 | TCCR1 = 0; // Select no clock 488 | GTCCR = 0; // Disconnect OC1B + !OC1B 489 | digitalWrite(TONE_PIN_INVERTED, LOW); 490 | digitalWrite(TONE_PIN, LOW); 491 | } 492 | 493 | /* 494 | * plays alarm signal for the specified seconds 495 | */ 496 | void playAlarmSignalSeconds(uint16_t aSecondsToPlay) { 497 | #if defined(DEBUG) 498 | Serial.print(F("Play alarm for ")); 499 | Serial.print(aSecondsToPlay); 500 | Serial.println(F(" seconds")); 501 | #endif 502 | uint16_t tCounter = (aSecondsToPlay * 10) / 13; // == ... * 1000 (ms per second) / (1300 ms for a loop) 503 | if (tCounter == 0) { 504 | tCounter = 1; 505 | } 506 | while (tCounter-- != 0) { 507 | // activate LED 508 | digitalWrite(LED_PIN, 1); 509 | PWMtone(OPEN_WINDOW_ALARM_FREQUENCY_LOW, 300); 510 | 511 | // deactivate LED 512 | digitalWrite(LED_PIN, 0); 513 | PWMtone(OPEN_WINDOW_ALARM_FREQUENCY_HIGH, 1000); 514 | } 515 | } 516 | 517 | void resetHistory() { 518 | for (uint8_t i = 0; i < TEMPERATURE_ARRAY_SIZE - 1; ++i) { 519 | sTemperatureArray[i] = 0; 520 | } 521 | } 522 | 523 | void readTempAndManageHistory() { 524 | sTemperatureNewSum = 0; 525 | sTemperatureOldSum = 0; 526 | uint8_t tIndex = TEMPERATURE_ARRAY_SIZE - 1; 527 | /* 528 | * shift values in temperature history array and insert new one at [0] 529 | */ 530 | while (tIndex >= TEMPERATURE_COMPARE_AMOUNT + TEMPERATURE_COMPARE_DISTANCE) { 531 | // shift TEMPERATURE_COMPARE_AMOUNT values to end and sum them up 532 | sTemperatureArray[tIndex] = sTemperatureArray[tIndex - 1]; 533 | sTemperatureOldSum += sTemperatureArray[tIndex - 1]; 534 | tIndex--; 535 | } 536 | while (tIndex >= TEMPERATURE_COMPARE_AMOUNT) { 537 | // shift values to end 538 | sTemperatureArray[tIndex] = sTemperatureArray[tIndex - 1]; 539 | tIndex--; 540 | } 541 | while (tIndex > 0) { 542 | // shift (TEMPERATURE_COMPARE_AMOUNT - 1) values to end and sum them up 543 | sTemperatureArray[tIndex] = sTemperatureArray[tIndex - 1]; 544 | sTemperatureNewSum += sTemperatureArray[tIndex - 1]; 545 | tIndex--; 546 | } 547 | /* 548 | * Read new Temperature 16 times (typical 280 - 320 at 25 C) and add to sum 549 | * needs 2 ms 550 | */ 551 | sTemperatureArray[0] = readADCChannelWithReferenceOversample(ADC_TEMPERATURE_CHANNEL_MUX, INTERNAL1V1, 4); 552 | sTemperatureNewSum += sTemperatureArray[0]; 553 | 554 | #if defined(DEBUG) 555 | // needs 4.4 ms 556 | Serial.print(F("Temp=")); 557 | Serial.print(sTemperatureArray[0]); 558 | Serial.print(F(" Old=")); 559 | Serial.print(sTemperatureOldSum); 560 | Serial.print(F(" New=")); 561 | Serial.println(sTemperatureNewSum); 562 | #endif 563 | } 564 | 565 | /* 566 | * Check if history is completely filled and if temperature is rising 567 | */ 568 | bool checkForTemperatureRising() { 569 | if (sTemperatureArray[TEMPERATURE_ARRAY_SIZE - 1] != 0 570 | && sTemperatureNewSum > sTemperatureOldSum + (TEMPERATURE_DELTA_THRESHOLD_DEGREE * TEMPERATURE_COMPARE_AMOUNT)) { 571 | #if defined(DEBUG) 572 | Serial.println(F("Alarm - detected window already closed -> start again")); 573 | #endif 574 | sOpenWindowDetected = false; 575 | resetHistory(); 576 | return true; 577 | } 578 | return false; 579 | } 580 | 581 | /* 582 | * Generates a 2200 | 1100 Hertz tone signal for 600 seconds / 10 minutes and then play it 10 seconds with intervals starting from 24 seconds up to 5 minutes. 583 | * After 2 minutes the temperature is checked for the remaining 8 minutes if temperature is increasing in order to detect a closed window. 584 | * Check temperature at each end of break interval to discover closed window, if window was closed during the silent break, but device was not reset. 585 | */ 586 | void alarm() { 587 | 588 | // First 120 seconds - just generate alarm tone 589 | playAlarmSignalSeconds(120); 590 | // after 80 seconds the new (increased) temperature is stable 591 | 592 | // prepare for new temperature check - reset history 593 | #if defined(DEBUG) 594 | Serial.println(F("After 120 seconds prepare for new temperature check -> reset history")); 595 | Serial.println(F("Play alarm for 480 seconds and check for rising temperature every 30 seconds")); 596 | #endif 597 | resetHistory(); 598 | 599 | // remaining 480 seconds - check temperature while generating alarm tone 600 | for (uint8_t i = 0; i < 16; ++i) { 601 | readTempAndManageHistory(); 602 | /* 603 | * Check if history is completely filled and if temperature is rising 604 | */ 605 | if (checkForTemperatureRising()) { 606 | return; 607 | } 608 | playAlarmSignalSeconds(30); 609 | } 610 | 611 | #if defined(DEBUG) 612 | Serial.println(F("After 10 minutes continuous alarm play it now for 10 seconds with increasing delay starting at 24 seconds")); 613 | #endif 614 | 615 | uint16_t tDelay = 24; // begin with 24 s, end at 600 s (5 minutes) 616 | /* 617 | * Do not check for rising temperature here, since it may break a valid alarm. 618 | * The alarm last for 10 minutes now and no rising temperature could be detected in this time, so it makes no sense here. 619 | */ 620 | while (true) { 621 | #if defined(DEBUG) 622 | Serial.print(F("Alarm pause for ")); 623 | Serial.print(tDelay); 624 | Serial.println(F(" seconds")); 625 | #endif 626 | sleepDelay(tDelay); // Start with 24 seconds 627 | playAlarmSignalSeconds(10); 628 | noTone(TONE_PIN); 629 | if (tDelay < 600) { // up to 5 minutes 630 | tDelay += tDelay / 16; 631 | } 632 | 633 | } 634 | } 635 | 636 | void playDoubleClick() { 637 | PWMtone(OPEN_WINDOW_ALARM_FREQUENCY_HIGH, 2); 638 | delayMilliseconds(100); // delay between clicks 639 | PWMtone(OPEN_WINDOW_ALARM_FREQUENCY_HIGH, 2); 640 | } 641 | 642 | /* 643 | * Flash LED only for a short period to save power. 644 | * If open window detected, increase pulse length to give a visual feedback 645 | */ 646 | void delayAndSignalOpenWindowDetectionAndLowVCC() { 647 | if (sOpenWindowDetected) { 648 | sOpenWindowDetectedOld = true; 649 | PWMtone(OPEN_WINDOW_ALARM_FREQUENCY_HIGH, 2); // 2 ms can be heard as a click 650 | delayMicroseconds(20000); // to let the led light longer 651 | 652 | } else if (sOpenWindowDetectedOld) { 653 | // closing window just detected -> signal it with 2 clicks 654 | sOpenWindowDetectedOld = false; // do it once 655 | playDoubleClick(); 656 | 657 | } else if (sVCCVoltageTooLow) { 658 | PWMtone(OPEN_WINDOW_ALARM_FREQUENCY_VCC_TOO_LOW, 40); // Use also a different frequency to distinguish this alert from others 659 | } else { 660 | delayMicroseconds(LED_PULSE_LENGTH - 150); // - 150 for the duration from digitalWrite(LED_PIN, 1) until here 661 | } 662 | } 663 | 664 | /* 665 | * In Power Down sleep mode we have the watchdog running and ADC disabled, but powered. 666 | * This needs 5.6 uA. 667 | * If BOD is enabled by fuses, we need additionally 20 uA resulting in 26 uA current. 668 | */ 669 | void sleepDelay(uint16_t aSecondsToSleep) { 670 | ADCSRA = 0; // disable ADC -> saves 200 uA 671 | for (uint16_t i = 0; i < (aSecondsToSleep / 8); ++i) { 672 | /* 673 | * Turn off the brown-out detector - but this works only for ATtiny85 revision C, which is hardly seen in the wild :-(. 674 | * It can save additional 20 uA if BOD is enabled by fuses 675 | */ 676 | sleep_bod_disable() 677 | ; 678 | // uint8_t tMcucrValue = MCUCR | _BV(BODS) | _BV(BODSE); // set to one 679 | // MCUCR = tMcucrValue; // set both flags to one 680 | // MCUCR = tMcucrValue & ~_BV(BODSE); // reset BODSE within 4 clock cycles 681 | sleep_cpu() 682 | ; 683 | } 684 | } 685 | 686 | void delayMilliseconds(unsigned int aMillis) { 687 | for (unsigned int i = 0; i < aMillis; ++i) { 688 | delayMicroseconds(1000); 689 | } 690 | } 691 | 692 | #define ADC_PRESCALE8 3 // 104 microseconds per ADC conversion at 1 MHz 693 | uint16_t readADCChannelWithReferenceOversample(uint8_t aChannelNumber, uint8_t aReference, uint8_t aOversampleExponent) { 694 | uint16_t tSumValue = 0; 695 | ADMUX = aChannelNumber | (aReference << SHIFT_VALUE_FOR_REFERENCE); 696 | 697 | // ADCSRB = 0; // free running mode if ADATE is 1 - is default 698 | // ADSC-StartConversion ADATE-AutoTriggerEnable ADIF-Reset Interrupt Flag 699 | ADCSRA = (_BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIF) | ADC_PRESCALE8); 700 | 701 | for (uint8_t i = 0; i < _BV(aOversampleExponent); i++) { 702 | /* 703 | * wait for free running conversion to finish. 704 | * Do not wait for ADSC here, since ADSC is only low for 1 ADC Clock cycle on free running conversion. 705 | */ 706 | loop_until_bit_is_set(ADCSRA, ADIF); 707 | 708 | ADCSRA |= _BV(ADIF); // clear bit to recognize conversion has finished 709 | // Add value 710 | tSumValue += ADCL | (ADCH << 8); 711 | // tSumValue += (ADCH << 8) | ADCL; // this does NOT work! 712 | } 713 | ADCSRA &= ~_BV(ADATE); // Disable auto-triggering (free running mode) 714 | return (tSumValue >> aOversampleExponent); 715 | } 716 | 717 | uint16_t getVCCVoltageMillivolt(void) { 718 | // use AVCC with external capacitor at AREF pin as reference 719 | uint8_t tOldADMUX = ADMUX; 720 | /* 721 | * Must wait >= 200 us if reference has to be switched to VSS 722 | * Must wait >= 70 us if channel has to be switched to ADC_1_1_VOLT_CHANNEL_MUX 723 | */ 724 | if ((ADMUX & (INTERNAL << SHIFT_VALUE_FOR_REFERENCE)) || ((ADMUX & 0x0F) != ADC_1_1_VOLT_CHANNEL_MUX)) { 725 | // switch AREF 726 | ADMUX = ADC_1_1_VOLT_CHANNEL_MUX | (DEFAULT << SHIFT_VALUE_FOR_REFERENCE); 727 | // and wait for settling 728 | delayMicroseconds(400); // experimental value is > 200 us 729 | } 730 | uint16_t tVCC = readADCChannelWithReferenceOversample(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 2); 731 | ADMUX = tOldADMUX; 732 | /* 733 | * Do not wait for reference to settle here, since it may not be necessary 734 | */ 735 | return ((1024L * 1100) / tVCC); 736 | } 737 | 738 | /* 739 | * called every hour 740 | * needs 4.5 ms 741 | */ 742 | void checkVCCPeriodically() { 743 | sVCCMonitoringDelayCounter--; 744 | if (sVCCMonitoringDelayCounter == 0) { 745 | sVCCVoltageMillivolt = getVCCVoltageMillivolt(); 746 | #if defined(DEBUG) 747 | Serial.print(F("VCC=")); 748 | Serial.print(sVCCVoltageMillivolt); 749 | Serial.print(F("mV - ")); 750 | if (sVCCVoltageMillivolt > VCC_VOLTAGE_USB_DETECTION) { 751 | Serial.print(F("USB")); 752 | } else { 753 | if (sLIPOSupplyDetected) { 754 | Serial.print(F("LIPO")); 755 | } else { 756 | Serial.print(F("standard or button cell")); 757 | } 758 | } 759 | Serial.println(" detected"); 760 | #endif 761 | if ((sLIPOSupplyDetected && sVCCVoltageMillivolt < VCC_VOLTAGE_LOWER_LIMIT_MILLIVOLT_LIPO) 762 | || (!sLIPOSupplyDetected 763 | && ((sBODLevelIsBelow2_7 && sVCCVoltageMillivolt < VCC_VOLTAGE_LOWER_LIMIT_MILLIVOLT_STANDARD) 764 | || (!sBODLevelIsBelow2_7 && sVCCVoltageMillivolt < VCC_VOLTAGE_LOWER_LIMIT_MILLIVOLT_STANDARD_BOD2_7))) 765 | 766 | ) { 767 | sVCCVoltageTooLow = true; 768 | sVCCMonitoringDelayCounter = 4; // VCC too low -> check every 2 minutes 769 | } else { 770 | sVCCVoltageTooLow = false; 771 | sVCCMonitoringDelayCounter = (VCC_MONITORING_DELAY_MIN * 60) / TEMPERATURE_SAMPLE_SECONDS; // VCC OK -> check every hour 772 | } 773 | } 774 | } 775 | 776 | /* 777 | * Watchdog wakes CPU periodically and all we have to do is call sleep_cpu(); 778 | * aWatchdogPrescaler (see wdt.h) can be one of 779 | * WDTO_15MS, 30, 60, 120, 250, WDTO_500MS 780 | * WDTO_1S to WDTO_8S 781 | */ 782 | void initPeriodicSleepWithWatchdog(uint8_t tSleepMode, uint8_t aWatchdogPrescaler) { 783 | sleep_enable() 784 | ; 785 | set_sleep_mode(tSleepMode); 786 | MCUSR = ~_BV(WDRF); // Clear WDRF in MCUSR 787 | 788 | #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) 789 | #define WDTCSR WDTCR 790 | #endif 791 | // Watchdog interrupt enable + reset interrupt flag -> needs ISR(WDT_vect) 792 | uint8_t tWDTCSR = _BV(WDIE) | _BV(WDIF) | (aWatchdogPrescaler & 0x08 ? _WD_PS3_MASK : 0x00) | (aWatchdogPrescaler & 0x07); // handles that the WDP3 bit is in bit 5 of the WDTCSR register, 793 | WDTCSR = _BV(WDCE) | _BV(WDE); // clear lock bit for 4 cycles by writing 1 to WDCE AND WDE 794 | WDTCSR = tWDTCSR; // set final Value 795 | } 796 | 797 | /* 798 | * This interrupt wakes up the cpu from sleep 799 | */ 800 | ISR(WDT_vect) { 801 | ; 802 | } 803 | 804 | uint8_t getBODLevelFuses() { 805 | return boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS) & (~FUSE_BODLEVEL2 | ~FUSE_BODLEVEL1 | ~FUSE_BODLEVEL0 ); 806 | } 807 | 808 | /* 809 | * @return true if BODS flag is existent - should be true for ATtiny85 revision C and later 810 | */ 811 | bool isBODSFlagExistent() { 812 | sleep_bod_disable() 813 | ; 814 | /* 815 | * Check if flag can be set - this works only for ATtini85 revision C, which is hardly seen in the wild. 816 | */ 817 | return MCUCR & _BV(BODS); 818 | } 819 | 820 | #if defined(DEBUG) 821 | /* 822 | * Output description for all fuses except "DebugWire enabled" 823 | */ 824 | void printFuses(void) { 825 | uint8_t tLowFuseBits = boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS); 826 | Serial.print(F("\nLowFuses=")); 827 | Serial.printHex(tLowFuseBits); 828 | 829 | Serial.print(F("\nClock divide by 8 ")); 830 | if (tLowFuseBits & ~FUSE_CKDIV8) { 831 | Serial.print(F("not ")); 832 | } 833 | Serial.print(F("enabled\n")); // enabled is default 834 | 835 | Serial.print(F("Clock output ")); 836 | if (tLowFuseBits & ~FUSE_CKOUT) { 837 | Serial.print(F("not ")); 838 | } 839 | Serial.print(F("enabled\n")); // enabled is default 840 | 841 | Serial.print(F("Clock select=")); 842 | uint8_t tClockSelectBits = tLowFuseBits & (~FUSE_CKSEL3 | ~FUSE_CKSEL2 | ~FUSE_CKSEL1 | ~FUSE_CKSEL0 ); 843 | switch (tClockSelectBits) { 844 | case 1: 845 | Serial.print(F("16MHz PLL")); 846 | break; 847 | case 2: 848 | Serial.print(F("8MHz")); // default 849 | break; 850 | case 3: 851 | Serial.print(F("6.4MHz")); 852 | break; 853 | case 4: 854 | Serial.print(F("128kHz")); 855 | break; 856 | case 6: 857 | Serial.print(F("32.768kHz")); 858 | break; 859 | default: 860 | Serial.print(F("External")); 861 | break; 862 | } 863 | 864 | Serial.print(F("\nStart-up time=")); 865 | uint8_t tStartUptimeBits = tLowFuseBits & (~FUSE_SUT1 | ~FUSE_SUT0 ); 866 | if (tClockSelectBits == 1) { 867 | /* 868 | * PLL Clock has other interpretation of tStartUptimeBits 869 | */ 870 | Serial.print(F("14 CK (+ 4ms)")); 871 | if (tLowFuseBits & ~FUSE_SUT0) { 872 | Serial.print(F("+ 16384 CK")); // -> 1 ms for 16 MHz clock 873 | } else { 874 | Serial.print(F(" + 1024 CK")); 875 | } 876 | if (tLowFuseBits & ~FUSE_SUT1) { 877 | Serial.print(F(" + 64ms")); // default 878 | } else { 879 | Serial.print(F(" + 4ms")); 880 | } 881 | } else { 882 | /* 883 | * Internal Calibrated RC Oscillator Clock 884 | */ 885 | Serial.print(F("6 (+ 14 CK)")); 886 | switch (tStartUptimeBits) { 887 | case 0x10: 888 | Serial.print(F(" + 4ms")); 889 | break; 890 | case 0x20: 891 | Serial.print(F(" + 64ms")); // default 892 | break; 893 | default: 894 | break; 895 | } 896 | } 897 | 898 | uint8_t tHighFuseBits = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS); 899 | Serial.print(F("\n\nHighFuses=")); 900 | Serial.printHex(tHighFuseBits); 901 | 902 | Serial.print(F("\nReset ")); 903 | if (tHighFuseBits & ~FUSE_RSTDISBL) { 904 | Serial.print(F("not ")); 905 | } 906 | Serial.print(F("disabled\n")); 907 | 908 | Serial.print(F("Serial programming ")); 909 | if (tHighFuseBits & ~FUSE_SPIEN) { 910 | Serial.print(F("not ")); 911 | } 912 | Serial.print(F("enabled\n")); 913 | 914 | Serial.print(F("Watchdog always on ")); 915 | if (tHighFuseBits & ~FUSE_WDTON) { 916 | Serial.print(F("not ")); 917 | } 918 | Serial.print(F("enabled\n")); 919 | 920 | Serial.print(F("Preserve EEPROM ")); 921 | if (tHighFuseBits & ~FUSE_EESAVE) { 922 | Serial.print(F("not ")); 923 | } 924 | Serial.print(F("enabled\n")); 925 | 926 | Serial.print(F("Brown-out=")); 927 | uint8_t tBrownOutDetectionBits = tHighFuseBits & (~FUSE_BODLEVEL2 | ~FUSE_BODLEVEL1 | ~FUSE_BODLEVEL0 ); 928 | switch (tBrownOutDetectionBits) { 929 | // 0-3 are reserved codes (for ATtiny) 930 | case 4: 931 | Serial.print(F("4.3V")); 932 | break; 933 | case 5: 934 | Serial.print(F("2.7V")); 935 | break; 936 | case 6: 937 | Serial.print(F("1.8V")); 938 | break; 939 | case 7: 940 | Serial.print(F("disabled")); 941 | break; 942 | default: 943 | break; 944 | } 945 | 946 | uint8_t tExtFuseBits = boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS); 947 | Serial.print(F("\n\nExtFuses=")); 948 | Serial.printHex(tExtFuseBits); 949 | Serial.print(F("\nSelf programming ")); 950 | if (tExtFuseBits & ~FUSE_SELFPRGEN) { 951 | Serial.print(F("not ")); 952 | } 953 | Serial.println(F("enabled\n")); 954 | } 955 | 956 | void printBODSFlagExistence() { 957 | Serial.print(F("BODS flag ")); 958 | if (!isBODSFlagExistent()) { 959 | Serial.print(F("not ")); 960 | } 961 | Serial.println(F("existent")); 962 | } 963 | 964 | #endif 965 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # [OpenWindowAlarm](https://github.com/ArminJo/Arduino-OpenWindowAlarm) 4 | Place this gadget on a windowsill and you will be alarmed if you leave the window open longer than five minutes. 5 | It senses the falling temperature and thus works best in winter. It requires only 0.026 milliampere. This means one battery will last the whole winter. 6 | 7 | [![Badge License: GPLv3](https://img.shields.io/badge/License-GPLv3-brightgreen.svg)](https://www.gnu.org/licenses/gpl-3.0) 8 |     9 | [![Badge Version](https://img.shields.io/github/v/release/ArminJo/Arduino-OpenWindowAlarm?include_prereleases&color=yellow&logo=DocuSign&logoColor=white)](https://github.com/ArminJo/Arduino-OpenWindowAlarm/releases/latest) 10 |     11 | [![Badge Commits since latest](https://img.shields.io/github/commits-since/ArminJo/Arduino-OpenWindowAlarm/latest?color=yellow)](https://github.com/ArminJo/Arduino-OpenWindowAlarm/commits/master) 12 |     13 | [![Badge Build Status](https://github.com/ArminJo/Arduino-OpenWindowAlarm/workflows/TestCompile/badge.svg)](https://github.com/ArminJo/Arduino-OpenWindowAlarm/actions) 14 |     15 | ![Badge Hit Counter](https://visitor-badge.laobi.icu/badge?page_id=ArminJo_Arduino-OpenWindowAlarm) 16 |
17 |
18 | [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua) 19 | 20 |
21 | 22 | Available as "OpenWindowAlarm" example of Arduino library "ATtinySerialOut" 23 | 24 | #### If you find this library useful, please give it a star. 25 | 26 | 🌎 [Google Translate](https://translate.google.com/translate?sl=en&u=https://github.com/ArminJo/Arduino-OpenWindowAlarm) 27 | 28 |
29 | 30 | ### [Driver installation ->](https://github.com/ArminJo/Arduino-OpenWindowAlarm#driver-installation) 31 | 32 | ### Sourcecode 33 | Der **Sourcecode** kann von [hier](https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/master/OpenWindowAlarm/OpenWindowAlarm.ino) kopiert werden.
34 | Das Programm ist auch als Beispiel der Arduino "ATtinySerialOut" Bibliothek - unter *Datei -> Beispiele -> Beispiele aus eigenen Bibliotheken - ATtinySerialOut -> OpenWindowAlarm* verf�gbar. Die Bibliothek kann mit *Werkzeuge -> Bibliotheken verwalten...* oder *Strg+Umschalt+I* geladen werden. Dabei "SerialOut" als Suchstring benutzen. 35 | 36 | The **sourcecode** can be copied from [here](https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/master/OpenWindowAlarm/OpenWindowAlarm.ino).
37 | The application is also available as an example of the [Arduino ATtinySerialOut library](https://github.com/ArminJo/ATtinySerialOut) - use *File -> Examples -> Examples from Custom Libraries - ATtinySerialOut -> OpenWindowAlarm*. 38 | You can load the library with *Tools -> Manage Libraries...* or *Ctrl+Shift+I*. Use "SerialOut" as filter string.
39 | 40 | | YouTube video | Instructable | 41 | |---------|------| 42 | | [![YouTube video](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/OpenWindowAlarm1.jpg)](https://youtu.be/6l_QOM59nyc)
OpenWindowAlarm on a windowsill | [![Instructable](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/instructables-logo-v2.png)](https://www.instructables.com/id/Arduino-Open-Window-Detector-for-Winter/) | 43 | 44 | # Functional overview 45 | Every 24 seconds a reading is taken of the ATtiny internal temperature sensor which has a resolution of 1 degree. 46 | If temperature is lower than the "old" temperature value, an **alarm is issued five minutes later** if by then the condition still holds true.
47 | **Detection of an open window** is indicated by a longer 20 ms blink and a short click every 24 seconds. 48 | **Low battery** is indicated by beeping and flashing the LED every 24 seconds. The beep and the flash are longer than for an open window detection. 49 | 50 | # How to make your own 51 | ### The parts you need: 52 | ![Parts](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/Parts.jpg) 53 | ### Add one of the power supplies 54 | | AAA battery case | CR2032 case | LiPo battery | 55 | |---|---|---| 56 | | ![AAA battery case](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/BatteryCase.jpg) | ![CR2032 holder](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/CR2032Holder.jpg) | ![LiPo battery](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/LiPo.jpg) | 57 | 58 | ## Programming the Digispark board 59 | ### Installation of the core for Digispark boards for the Arduino IDE 60 | Install the Digispark board for the Arduino IDE as described in http://digistump.com/wiki/digispark/tutorials/connecting. Instead of **http://digistump.com/package_digistump_index.json** you must use **http://drazzy.com/package_drazzy.com_index.json** as Digispark board URL in Arduino *File/Preferences*. In the *Boards Manager* install the latest **ATTinyCore** version.
61 | Since we want to save power, the board clock is 1 MHz so you must choose **ATTinyCore->ATtiny85 (Micronucleus / DigiSpark)** 62 | as board in the *Tools* menu and set the clock to **1MHz (no USB)**. 63 | ![Board settings](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/BoardSettings.png) 64 | 65 | ## Driver installation 66 | For Windows you must install the **Digispark driver** before you can program the board.
67 | if you have the *Digistump AVR Boards* already installed, then the driver is located in `%UserProfile%\AppData\Local\Arduino15\packages\ATTinyCore\tools\micronucleus\2.5-azd1b`. Just execute the `Install_Digistump_Drivers.bat` file.
68 | **Or** download it [here](https://github.com/digistump/DigistumpArduino/releases/download/1.6.7/Digistump.Drivers.zip), open it and run `InstallDrivers.exe`. 69 | 70 | ### German instructions 71 | Leider muss der Treiber f�r das Digispark Board manuell installiert werden. Der **Digispark Treiber** kann von [hier](https://github.com/digistump/DigistumpArduino/releases/download/1.6.7/Digistump.Drivers.zip) heruntergeladen werden. Dann die Datei �ffnen und `InstallDrivers.exe` ausf�hren.
72 | Wenn die Digispark Boards in der Arduino IDE schon installiert sind, ist der Treiber bereits auf der Platte unter `%UserProfile%\AppData\Local\Arduino15\packages\ATTinyCore\tools\micronucleus\2.5-azd1b`. Am einfachsten installiert man ihn, wenn man das Board einsteckt und wenn das unbekannte Ger�t im Ger�te-Manager auftaucht, *Treiber aktualisieren* ausw�hlt. Dann *Auf dem Computer nach Treibersoftware suchen* w�hlen, `C:\Users\` w�hlen und *Weiter* klicken.
73 | Bei der Nachfrage *M�chten sie diese Ger�tesoftware installieren* auf *installieren* klicken. 74 | 75 | Wenn das **Board nicht erkannt** wird (kein Ger�usch beim Einstecken) kann es daran liegen, dass die Buchse zu tief ist, dann eine ander Buchse oder ein USB Verl�ngerungskabel benutzen. 76 | 77 | ### Compile and upload the program to the board 78 | Install the Arduino library **ATtinySerialOut** and select the OpenWindowsAlarm example with *File -> Examples -> Examples from Custom Libraries -ATtinySerialOut -> OpenWindowAlarm*
79 | **OR**
80 | create a new sketch with *File -> New* and name it `OpenWindowAlarm` in the Arduino IDE and copy the code from [OpenWindowAlarm.ino](https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/master/OpenWindowAlarm/OpenWindowAlarm.ino). 81 | 82 | Compile and upload it. Keep in mind, that **upload will not work if the speaker is connected**.
83 | If everything works well, the built-in LED of the Board will blink 5 times (for the 5 minutes alarm delay) and then start flashing after 8 seconds with an interval of 24 seconds to signal each temperature reading. 84 | 85 | ## Power reduction 86 | Before power reduction changes 87 | ![Final power reduction](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/Digispark.jpg) 88 | 89 | We now have a Digispark board that [consumes 6 mA at 1MHz and 3,7 volt](https://github.com/ArminJo/micronucleus-firmware#measured-digispark-pll-clock-supply-current). With a battery of **2000 mAh** it will run for **14 days**. But it is possible to reduce power consumption to **27 �A** in 3 Steps. 90 | 1. **Disabling the power LED** by breaking the copper wire that connects the power LED to the diode with a knife or removing / disabling the 102 resistor saves 2/2.2 mA. 91 | 2. **Removing the VIN voltage regulator** saves 1.5/3.0 mA.
92 | The board now needs 3/4.3 mA at 3.7/5 volt and the 2000mAh battery will last for 28 days. 93 | 3. **Disconnecting the USB D- Pullup resistor** (marked 152) from 5 volt (VCC). Disconnect it by breaking the copper wire on the side of the resistor that points to the ATtiny.
94 | **This disables the USB interface** and in turn the possibility to program the Digispark board via USB. To **enable it again**, but still save power, **connect the resistor (marked 152) directly to the USB V+** that is easily available at the outer side of the diode.
95 | The correct side of the diode can be found by using a continuity tester. One side of this diode is connected to pin 8 of the ATtiny (VCC) and Digispark 5V. The other side is connected to the USB V+. 96 | 97 | Now the USB pullup resistor is only activated if the Digispark board is connected to USB e.g. during programming.
98 | The board now consumes **27 �A** during sleep. 99 | 100 | The software loop needs 2.1 ms (plus 3 times 64 ms startup time) => active time is around 1/125 of total time. 101 | During the loop the power consumption is 100 times the sleep current => Loop adds **80%** to total power consumption.
102 | We now have an average current consumption of **75 �A** and the 2000mAh battery will last for **3 years**. 103 | 104 | The BOD current of 20 �A can only be disabled by setting fuses via ISP programmer](https://www.google.de/search?q=arduino+as+isp) and a connecting adapter. We can also reduce the start-up time from sleep from 64 to to 5 ms. 105 | For reprogramming the fuses, you can use [this script](https://github.com/ArminJo/micronucleus-firmware/blob/master/utils/Write%2085%20Fuses%20E1%20DF%20FE%20-%20Digispark%20default%20without%20BOD%20and%20Pin5%20and%20fast%20startup.cmd).
106 | Without BOD and with fast startup we have an average current consumption of **9 �A** and are still able to program the ATtiny by USB. 107 | 108 | ## Reset button 109 | **If you do not want to remove power to reset the alarm**, connect a reset button between PB5 and ground. 110 | I did this by connecting the unconnected VIN copper surface to PB5 and soldering the reset button directly to the VIN pin hole and the big ground surface of the removed VIN voltage regulator.

111 | If you want to **get rid of the 5 seconds wait** for USB connection **after reset**, you can [change the micronucleus kernel on the ATtiny85](https://github.com/ArminJo/DigistumpArduino#update-the-bootloader). 112 | 113 | ### After power reduction changes and reset button assembly 114 | | Both patches on front | Reset connection on back | 115 | |---|---| 116 | | ![Final power reduction](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/Final-Version-Detail_annotated.jpg) | ![Patch front](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/PatchWithResetOnBack.jpg) | 117 | | ![Final power reduction](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/Patch1.jpg) | ![Reset connection on back](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/ResetConnectionBack.jpg) | 118 | | | ![Mini-USB module](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/MiniUSBModule.jpg) | 119 | 120 | ## Loudspeaker disassembly 121 | | Part 1 | Part 2 | 122 | |---|---| 123 | | ![Loudspeaker disassembly](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/Loudspeaker1.jpg) | ![Loudspeaker disassembly](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/Loudspeaker2.jpg) | 124 | 125 | 126 | ## Module samples 127 | | | | 128 | |---|---| 129 | | ![OpenWindowAlarm circuit with AAA batteries](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/Final-VersionAAA.jpg)
Powered by 2 AAA batteries | ![OpenWindowAlarm circuit by CR2032](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/CR2032Front.jpg)
Powered by CR2032 coin cell | 130 | | ![OpenWindowAlarm circuit with LiPo battery](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/Final-VersionLiPo.jpg)
Powered by LiPo battery | ![OpenWindowAlarm circuit by CR2032](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/CR2032Back.jpg)
Back viev with CR2032 coin cell | 131 | | ![OpenWindowAlarm circuit with buzzer](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/OpenWindowAlarm2AAA.jpg)
With 16 Ω buzzer from an old Pc | ![OpenWindowAlarm circuit compact version](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/Final-Version-Compact.jpg)
Compact version | 132 | 133 | Different reset buttons and connectors 134 | ![OpenWindowAlarm circuit by CR2032](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/4Modules.jpg) 135 | 136 | # Placement 137 | Place the board on a windowsill and connect it to the supply. 138 | If the temperature on the sill is lower than the temperature where the board was originally located, it will take additional 5 minutes to adopt to the new start value to avoid false alarm. 139 | ![OpenWindowAlarm circuit with LiPo battery](https://github.com/ArminJo/Arduino-OpenWindowAlarm/blob/master/pictures/OpenWindowAlarmLiPo_2.jpg) 140 | 141 | # Internal operation 142 | * An open window is detected after `TEMPERATURE_COMPARE_AMOUNT * TEMPERATURE_SAMPLE_SECONDS` (48) seconds of reading a temperature with a value of `TEMPERATURE_DELTA_THRESHOLD_DEGREE` (2) lower 143 | than the temperature `TEMPERATURE_COMPARE_DISTANCE * TEMPERATURE_SAMPLE_SECONDS` (192 seconds-> 3 minutes and 12 seconds) before. 144 | * The delay is implemented by sleeping 3 times at `SLEEP_MODE_PWR_DOWN` for a period of 8 seconds -the maximum hardware sleep time- to reduce power consumption. 145 | 146 | * If an **open window is detected**, this is indicated by a longer **20 ms blink** and a **short click** every 24 seconds. 147 | Therefore, the internal sensor has a time of 3 minutes to adjust to the outer temperature in order to capture even small changes in temperature. 148 | The greater the temperature change the earlier the sensor value will change and detect an open window. 149 | 150 | * `OPEN_WINDOW_ALARM_DELAY_MINUTES` (5) minutes after open window detection the **alarm is activated**.
151 | The alarm will not start or an activated alarm will stop if the current temperature is greater than the minimum measured temperature (+ 1) i.e. the window has been closed already. 152 | 153 | * The **initial alarm** lasts for 10 minutes. After this, it is activated for a period of 10 seconds with a increasing break time from 24 seconds up to 5 minutes. 154 | 155 | * At **power-on** the VCC voltage is measured used to **determine the type of battery** using `VCC_VOLTAGE_LIPO_DETECTION` (3.6 volt). 156 | 157 | * Every `VCC_MONITORING_DELAY_MIN` (60) minutes the battery voltage is measured. Depending on the detected battery type, **low battery voltage** is indicated by **beeping and flashing the LED every 24 seconds**. 158 | Only the beep (not the flash) is significantly longer than the beep for an open window detection.
159 | Low battery voltage is defined by `VCC_VOLTAGE_LOWER_LIMIT_MILLIVOLT_LIPO` (3550 Millivolt) or `VCC_VOLTAGE_LOWER_LIMIT_MILLIVOLT_STANDARD` (2350 Millivolt). 160 | 161 | * After power-on, the **inactive settling time** is 5 minutes. If the board is getting colder during the settling time, 4:15 (or 8:30) minutes are added to avoid false alarms after power-on. 162 | 163 | * If you enable `DEBUG` by activating line 62, you can monitor the serial output with 115200 baud at P2 to see what is happening. 164 | 165 | # Revision History 166 | ### Version 1.3.3 167 | - 3. party libs. 168 | ### Version 1.3.2 169 | - Adapted MCUSR handling. 170 | ### Version 1.3.1 171 | - Check for closed window happens only the first 10 minutes of alarm. 172 | ### Version 1.3.0 173 | - Changed voltage low detection. 174 | - Improved DEBUG output. 175 | ### Version 1.2.2 176 | - Converted to Serial.print. 177 | - New PWMTone() without tone(). 178 | ### Version 1.2.1 179 | - Fixed bug in check for temperature rising after each alarm. 180 | ### Version 1.2 181 | - Improved sleep, detecting closed window also after start of alarm, reset behavior. 182 | - Changed LIPO detection threshold. 183 | - Fixed analog reference bug. 184 | 185 | ### Sample TRACE output after reset 186 | ``` 187 | Changed OSCCAL from 0x52 to 0x4e 188 | START ../src/OpenWindowAlarm.cpp 189 | Version 1.2.1 from Nov 5 2019 190 | Alarm delay = 5 minutes 191 | MCUSR=0x2 LFuse=0x225 WDTCR=0x0 OSCCAL=0x78 192 | Booting from reset 193 | VCC=4022mV - LIPO detected 194 | Temp=300 Old=0 New=300 195 | Temp=298 Old=0 New=598 196 | Temp=298 Old=0 New=596 197 | Temp=297 Old=0 New=595 198 | Temp=296 Old=0 New=593 199 | Temp=296 Old=0 New=592 200 | Temp=296 Old=0 New=592 201 | Temp=296 Old=0 New=592 202 | Temp=296 Old=0 New=592 203 | Temp=296 Old=0 New=592 204 | Temp=296 Old=300 New=592 205 | Detected porting to a colder place -> reset 206 | Temp=296 Old=0 New=296 207 | Temp=296 Old=0 New=592 208 | ... 209 | Temp=296 Old=0 New=592 210 | Temp=296 Old=296 New=592 211 | Temp=296 Old=592 New=592 212 | Temp=296 Old=592 New=592 213 | ``` 214 | #### If you find this program useful, please give it a star. 215 | -------------------------------------------------------------------------------- /SetPath.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | @echo Looking for Arduino installation at default paths 3 | @set ARDUINO_DIRECTORY="C:\Program Files (x86)\Arduino" 4 | @if exist %ARDUINO_DIRECTORY%\hardware\tools\avr\bin\avr-gcc.exe goto setPath 5 | @echo avr-gcc.exe not found in directory %ARDUINO_DIRECTORY%\hardware\tools\avr\bin\avr-gcc.exe 6 | @set ARDUINO_DIRECTORY="C:\Program Files\Arduino IDE" 7 | @if exist %ARDUINO_DIRECTORY%\hardware\tools\avr\bin\avr-gcc.exe goto setPath 8 | @echo avr-gcc.exe not found in directory %ARDUINO_DIRECTORY%\hardware\tools\avr\bin\avr-gcc.exe 9 | @set ARDUINO_DIRECTORY="%localappdata%Program Files\Arduino IDE" 10 | @if exist %ARDUINO_DIRECTORY%\hardware\tools\avr\bin\avr-gcc.exe goto setPath 11 | @echo avr-gcc.exe not found in directory %ARDUINO_DIRECTORY%\hardware\tools\avr\bin\avr-gcc.exe 12 | 13 | @set ARDUINO_DIRECTORY=E:\Elektro\arduino 14 | @echo Looking for Arduino installation at user defined directory %ARDUINO_DIRECTORY% 15 | @if exist %ARDUINO_DIRECTORY%\hardware\tools\avr\bin\avr-gcc.exe goto setPath 16 | @echo ERROR - avr-gcc.exe not found in directory %ARDUINO_DIRECTORY%\hardware\tools\avr\bin\avr-gcc.exe 17 | @pause 18 | @exit 19 | 20 | :setPath 21 | @echo Add Arduino binaries, Digispark launcher and our windows make.exe directory to path 22 | @set PATH=%ARDUINO_DIRECTORY%\hardware\tools\avr\bin;%UserProfile%\AppData\Local\Arduino15\packages\digistump\tools\micronucleus\2.0a4;..\windows_exe;%PATH% 23 | -------------------------------------------------------------------------------- /extras/Arduino-Open-Window-Detector-for-Winter.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/extras/Arduino-Open-Window-Detector-for-Winter.pdf -------------------------------------------------------------------------------- /pictures/4Modules.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/4Modules.jpg -------------------------------------------------------------------------------- /pictures/ArduinoDigisparkBoardSelection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/ArduinoDigisparkBoardSelection.png -------------------------------------------------------------------------------- /pictures/BatteryCase.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/BatteryCase.jpg -------------------------------------------------------------------------------- /pictures/BoardSettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/BoardSettings.png -------------------------------------------------------------------------------- /pictures/CR2032Back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/CR2032Back.jpg -------------------------------------------------------------------------------- /pictures/CR2032Front.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/CR2032Front.jpg -------------------------------------------------------------------------------- /pictures/CR2032Holder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/CR2032Holder.jpg -------------------------------------------------------------------------------- /pictures/CR3032.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/CR3032.jpg -------------------------------------------------------------------------------- /pictures/Digispark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/Digispark.jpg -------------------------------------------------------------------------------- /pictures/Final-Version-Compact.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/Final-Version-Compact.jpg -------------------------------------------------------------------------------- /pictures/Final-Version-Detail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/Final-Version-Detail.jpg -------------------------------------------------------------------------------- /pictures/Final-Version-Detail1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/Final-Version-Detail1.jpg -------------------------------------------------------------------------------- /pictures/Final-Version-Detail_annotated.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/Final-Version-Detail_annotated.jpg -------------------------------------------------------------------------------- /pictures/Final-VersionAAA.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/Final-VersionAAA.jpg -------------------------------------------------------------------------------- /pictures/Final-VersionLiPo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/Final-VersionLiPo.jpg -------------------------------------------------------------------------------- /pictures/LiPo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/LiPo.jpg -------------------------------------------------------------------------------- /pictures/Loudspeaker1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/Loudspeaker1.jpg -------------------------------------------------------------------------------- /pictures/Loudspeaker2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/Loudspeaker2.jpg -------------------------------------------------------------------------------- /pictures/MiniUSBModule.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/MiniUSBModule.jpg -------------------------------------------------------------------------------- /pictures/OpenWindowAlarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/OpenWindowAlarm.png -------------------------------------------------------------------------------- /pictures/OpenWindowAlarm1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/OpenWindowAlarm1.jpg -------------------------------------------------------------------------------- /pictures/OpenWindowAlarm2AAA.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/OpenWindowAlarm2AAA.jpg -------------------------------------------------------------------------------- /pictures/OpenWindowAlarmLiPo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/OpenWindowAlarmLiPo.jpg -------------------------------------------------------------------------------- /pictures/OpenWindowAlarmLiPo_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/OpenWindowAlarmLiPo_2.jpg -------------------------------------------------------------------------------- /pictures/Parts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/Parts.jpg -------------------------------------------------------------------------------- /pictures/PartsAll_annotated.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/PartsAll_annotated.jpg -------------------------------------------------------------------------------- /pictures/Patch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/Patch.jpg -------------------------------------------------------------------------------- /pictures/Patch1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/Patch1.jpg -------------------------------------------------------------------------------- /pictures/PatchAnnotated.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/PatchAnnotated.jpg -------------------------------------------------------------------------------- /pictures/PatchWithResetOnBack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/PatchWithResetOnBack.jpg -------------------------------------------------------------------------------- /pictures/ResetConnectionBack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/ResetConnectionBack.jpg -------------------------------------------------------------------------------- /pictures/instructables-logo-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArminJo/Arduino-OpenWindowAlarm/7d8a2228c0239aaf29ddac0f6cfdd831d27f959d/pictures/instructables-logo-v2.png -------------------------------------------------------------------------------- /pictures/readme.txt: -------------------------------------------------------------------------------- 1 | This folder contains only the pictures of the project. 2 | --------------------------------------------------------------------------------