├── .circleci └── config.yml ├── .github └── workflows │ └── SimpleBuild.yml ├── .gitignore ├── LICENSE ├── README.md ├── example ├── zForceKeyboardMouse │ └── zForceKeyboardMouse.ino └── zForceLibraryExampleCode │ └── zForceLibraryExampleCode.ino ├── keywords.txt ├── library.json ├── library.properties └── src ├── I2C ├── I2C.cpp └── I2C.h ├── Zforce.cpp └── Zforce.h /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | jobs: 4 | build: 5 | docker: 6 | - image: suculent/arduino-docker-build@sha256:74b4c95da9285e16b628c2d480bd3797621eb8aa36c9fc2b89c3aae185afa22d 7 | steps: 8 | - checkout 9 | - run: 10 | name: Run arduino 11 | command: | 12 | export PATH=/opt/arduino:$PATH 13 | mkdir -p ~/Arduino/libraries/ 14 | ln -s -r -f . ~/Arduino/libraries/Zforce 15 | arduino --pref build.path=build --verify example/zForceLibraryExampleCode/zForceLibraryExampleCode.ino -v 16 | ls -l build 17 | - store_artifacts: 18 | path: build/zForceLibraryExampleCode.ino.hex 19 | destination: example.hex 20 | workflows: 21 | version: 2 22 | build_and_test: 23 | jobs: 24 | - build 25 | -------------------------------------------------------------------------------- /.github/workflows/SimpleBuild.yml: -------------------------------------------------------------------------------- 1 | name: SimpleBuild 2 | on: push 3 | jobs: 4 | build: 5 | name: Test compiling examples 6 | runs-on: ubuntu-latest 7 | 8 | strategy: 9 | matrix: 10 | arduino-boards-fqbn: 11 | - arduino:avr:uno 12 | 13 | include: 14 | - arduino-boards-fqbn: arduino:avr:uno 15 | sketches-exclude: zForceKeyboardMouse 16 | 17 | - arduino-boards-fqbn: neonode:samd:euseniot_neonode_adapter 18 | platform-url: https://raw.githubusercontent.com/neonode-inc/zforce-arduino-board-devenv/master/package_neonode_index.json 19 | required-libraries: Keyboard,Mouse 20 | 21 | fail-fast: false 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | - name: Compile all examples 26 | uses: ArminJo/arduino-test-compile@v3 27 | with: 28 | arduino-board-fqbn: ${{ matrix.arduino-boards-fqbn }} 29 | platform-url: ${{ matrix.platform-url }} 30 | sketches-exclude: ${{ matrix.sketches-exclude }} 31 | required-libraries: ${{ matrix.required-libraries }} 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # VS Code Folder 2 | .vscode 3 | *.bak 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 489 | USA 490 | 491 | Also add information on how to contact you by electronic and paper mail. 492 | 493 | You should also get your employer (if you work as a programmer) or your 494 | school, if any, to sign a "copyright disclaimer" for the library, if 495 | necessary. Here is a sample; alter the names: 496 | 497 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 498 | library `Frob' (a library for tweaking knobs) written by James Random 499 | Hacker. 500 | 501 | , 1 April 1990 502 | Ty Coon, President of Vice 503 | 504 | That's all there is to it! 505 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Neonode Touch Sensor Module Interface library for Arduino 2 | 3 | An Arduino library for communicating with the _Neonode Touch Sensor Module_ optical touch sensor. Handles the fundamental BER encoded ASN.1 messages communicated over I2C. 4 | For more information, visit [Neonode homepage](https://neonode.com/). 5 | 6 | 7 | # Introduction 8 | The library offers an easy way to communicate with the sensor as well as some primitive parsing of the ASN.1 serialized response messages. This makes it easy to get x and y coordinates from touch notifications or set different settings in the sensor. The library does not have support for all messages available in the ASN.1 protocol, however methods for sending and receiving raw ASN.1 message are available and can be used to access the full functionality. 9 | 10 | 11 | # Support and Additional information 12 | Support information, user's guides, sensor product specification and much more can be found in our [Support Center](https://support.neonode.com/). 13 | 14 | - [Neonode Touch Sensor Module User's Guide](https://support.neonode.com/docs/pages/viewpage.action?pageId=101351508) 15 | - [zForce Message Specification](https://support.neonode.com/docs/display/AIRTSUsersGuide/Protocol+version+1.12) 16 | - [Configuration Parameters Overview](https://support.neonode.com/docs/display/AIRTSUsersGuide/Parameter+Overview) 17 | - [zForce Programmer](https://support.neonode.com/docs/display/ZFPUG/) 18 | 19 | # Supported Platforms and Hardware 20 | 21 | ## Platforms 22 | 23 | The library version 1.8 has been tested on and officially supports these platforms: 24 | - Arduino Uno Rev3 25 | - Raspberry Pi Pico 26 | 27 | Other platforms might work as well. 28 | 29 | ## Touch Sensor Modules 30 | All commercially available _Neonode Touch Sensor Modules_ are supported (this excludes customer specific firmwares). 31 | Neonode generally recommends using the latest firmware compatible with your sensor, visit [Neonode Support Center](https://support.neonode.com/) to download latest firmware. 32 | 33 | # How to Use the library 34 | 35 | ## Main Loop 36 | The library is built around using `zforce.GetMessage()` as the main method for reading messages from the sensor. The `GetMessage()` method checks if the data ready pin is high and, if it is, reads the awaiting message from the sensor. The received message is then parsed and a pointer to a `Message` is returned. 37 | A successful `GetMessage()` call will allocate memory for the new `Message` dynamically. It is up to the end user to destroy the message by calling `zforce.DestroyMessage()` when the message information is no longer needed. 38 | Please check the supplied example code for usage examples. 39 | 40 | ## Send and Read Messages 41 | The library has support for setting some basic configuration parameters in the sensor, for example `zforce.SetTouchActiveArea()`. When writing any message to the sensor, the end user has to make sure that data ready signal is `LOW` before writing (i.e. there must be no messages awaiting to be read from the sensor). If data ready signal is `HIGH`, `GetMessage()` method needs to be called until `nullptr` is received as response, indicating there are no more messages awaiting in the sensor. 42 | 43 | When a message has been sent, the sensor always creates a response that has to be read by the host. It could take some time for the sensor to create the response and put it in the I2C buffer, which is why it is recommended to call the `GetMessage()` method in a do-while loop after sending a request. 44 | 45 | ### Configuration of the Sensor 46 | #### 1.xx firmware 47 | Firmwares of versions 1.xx _do not_ support persistent storage of configuration parameters. The user must make sure the sensor is configured for the user's need after every startup and after every reset. A received message of type `BOOTCOMPLETE` indicates that the sensor has started up and is ready to be configured. See code example below. 48 | #### 2.xx firmware 49 | Firmware of versions 2.xx _do_ support both persistent storage of configuration parameters and configuration at runtime. Configuration parameters set at runtime will _not_ be stored persistently in the sensor. To alter the persistent configuration a separate tool is needed, see [zForce Programmer](https://support.neonode.com/docs/display/ZFPUG/) at [Neonode Support Center](https://support.neonode.com/). 50 | ### Configuration Parameters 51 | An overview of the configuration parameters can be found here: [Configuration Parameters Overview](https://support.neonode.com/docs/display/AIRTSUsersGuide/Parameter+Overview) 52 | 53 | **NOTE:** All configuration of the sensor should be done before sensor is initialized using `Start()` method. 54 | 55 | **Code Example:** 56 | ```C++ 57 | // Make sure that there is nothing in the I2C buffer before writing to the sensor 58 | Message* msg = zforce.GetMessage(); 59 | if(msg != nullptr) 60 | { 61 | // Here you can read whatever is in the message or just destroy it. 62 | zforce.DestroyMessage(msg); 63 | } 64 | 65 | // Send the Touch Active Area request 66 | zforce.TouchActiveArea(50,50,2000,4000); 67 | 68 | // Wait for the response to arrive 69 | do 70 | { 71 | msg = zforce.GetMessage(); 72 | } while (msg == nullptr); 73 | 74 | // See what the response contains 75 | if(msg->type == MessageType::TOUCHACTIVEAREATYPE) 76 | { 77 | Serial.print("minX is: "); 78 | Serial.println(((TouchActiveAreaMessage*)msg)->minX); 79 | Serial.print("minY is: "); 80 | Serial.println(((TouchActiveAreaMessage*)msg)->minY); 81 | Serial.print("maxX is: "); 82 | Serial.println(((TouchActiveAreaMessage*)msg)->maxX); 83 | Serial.print("maxY is: "); 84 | Serial.println(((TouchActiveAreaMessage*)msg)->maxY); 85 | } 86 | 87 | zforce.DestroyMessage(msg); 88 | ``` 89 | 90 | # Methods Overview 91 | 92 | 93 | ## Public Methods 94 | 95 | 96 | 97 | | Return Type | Method | Parameters |Description | Return | 98 | | --- | --- | --- | --- | --- | 99 | | Constructor | `Zforce` | None | Not used. | None | 100 | | `void` | `Start` | `int dataReady` | Initialize communication with the sensor including starting the I2C connection and configure the dataReady pin according to given parameter. Default sensor I2C address 0x50 will be used. | None | 101 | | `void` | `Start` | `int dataReady`, `int i2cAddress` | Initialize communication with the sensor including starting the I2C connection and configure the dataReady pin and I2C address according to provided parameters. | None | 102 | | `int` | `Read` | `uint8_t* payload` | Initiates an I2C read sequence. Response is copied to `payload` array. No parsing of the received message is done and no `Message` is created.
**CAUTION:** The user must ensure that sufficient space is available in `payload` to hold the complete I2C message.
*Recommendation:* For reading raw ASN.1 messages it is advised to use the `ReceiveRawMessage` method instead. | Error code according to the Atmel data sheet if an Atmel platform is used. 0 for success. Non-Atmel platforms will always return 0. | 103 | | `int` | `Write` | `uint8_t* payload` | Initiates an I2C write sequence. Data from `payload` array is sent.
*IMPORTANT:* For a successful write to the sensor, it is expected that `payload[0]` = `0xEE` and `payload[1]` = length of the subsequent ASN.1 message to send.
*Recommendation:* For sending raw ASN.1 messages, it is advised to use `SendRawMessage()` instead. | Error code according to the Atmel data sheet if an Atmel platform is used. 0 for success. Non-Atmel platforms will always return 0. | 104 | | `bool` | `SendRawMessage` | `uint8_t* payload`, `uint8_t payloadLength` | Sends a custom formatted raw ASN.1 message to the sensor. `payload` is a pointer to the buffer containing the ASN.1 message to send. `payloadLength` is the length of the message to send. `SendRawMessage` is the preferred method for writing custom ASN.1 serialized messages to the sensor. | `true` if successful, otherwise `false` *. | 105 | | `bool` | `ReceiveRawMessage` | `uint8_t* receivedLength`, `uint16_t *remainingLength` | Receive a raw ASN.1 message. No parsing of the message is done and no `Message` is created. The only validation done is decoding the initial ASN.1 payload length to see if more data should follow. `receivedLength` is a pointer to where the size of the returned data should be placed. `remainingLength` is a pointer to where the size of the remaining data should be placed, if any. `ReceiveRawMessage` is the preferred method for reading raw ASN.1 serialized messages directly from the sensor.
**CAUTION:** Any subsequent read or write operations, even reading notifications, touches, etc will _overwrite_ the message receive buffer, so make sure to copy any data you want to save. | If successful, a pointer to the ASN.1 payload of the received data is returned, otherwise `nullptr` is returned. The length of the message received is stored in `receivedLength` and length of remaining data the sensor has to send is stored in `remainingLength`. | 106 | | `uint8_t` | `Enable` | `bool isEnabled` | Enables the sensor for sending touch notifications. Operation mode is set to normal detection mode and sensor is enabled. | `true` if successful, otherwise `false` *.| 107 | | `bool` | `GetEnable` | `None` | Gets the current enable status of the sensor. Useful to make sure if there is a sensor connected or just a check to see if it is currently disabled or enabled. | `true` if successful, otherwise `false` *. | 108 | | `bool` | `TouchActiveArea` | `uint16_t minX`, `uint16_t minY`, `uint16_t maxX`, `uint16_t maxY` | Writes a touch active area configuration message to the sensor with the passed parameters. | `true` if the write succeeded, otherwise `false` *. | 109 | | `bool` | `FlipXY` | `bool isFlipped` | Writes a flip-xy configuration message to the sensor with the passed parameters. | `true` if the write succeeded, otherwise `false` *. | 110 | | `bool` | `ReverseX` | `bool isReversed` | Writes a reverse x configuration message to the sensor with the passed parameters. | `true` if the write succeeded, otherwise `false` *. | 111 | | `bool` | `ReverseY` | `bool isReversed` | Writes a reverse y configuration message to the sensor with the passed parameters. | `true` if the write succeeded, otherwise `false` *. | 112 | | `bool` | `Frequency` | `uint16_t idleFrequency`, `uint16_t fingerFrequency` | Writes a frequency configuration message to the sensor with the passed parameters. | `true` if the write succeeded, otherwise `false` *. | 113 | | `bool` | `ReportedTouches` | `uint8_t touches` | Writes a reported touches configuration message to the sensor with the passed parameters. | `true` if the write succeeded, otherwise `false` *. | 114 | | `bool` | `DetectionMode` | `bool mergeTouches`, `bool reflectiveEdgeFilter` | Writes a detection mode configuration message to the sensor with the passed parameters.
*NOTE:* Firmware versions 2.xx does _not_ support mergeTouches. | `true` if the write succeeded, otherwise `false` *. | 115 | | `bool` | `TouchMode` | `uint8_t mode`, `int16_t clickOnTouchRadius`, `int16_t clickOnTouchTime` | Writes a touchMode configuration message to the sensor with the passed parameters. Valid modes: 0 = normal, 1 = clickOnTouch.
*NOTE:* Some sensor firmware will not return clickOnTouchRadius or clickOnTouchTime in response message if mode is set = normal. In this case, these values will be set to -1 in the parsed response Message received using `GetMessage()` method.| `true` if the write succeeded, otherwise `false` *. | 116 | | `bool` | `FloatingProtection` | `bool enabled`, `uint16_t time` | Writes a floating protection configuration message to the sensor with the passed parameters. | `true` if the write succeeded, otherwise `false` *. | 117 | | `int` | `GetDataReady` | None | Performs a digital read on the data ready pin. | The current status of the data ready pin (`HIGH`/ `LOW`). | 118 | | `Message*` | `GetMessage` | None | Reads and parses a message from the sensor if data ready signal is `HIGH`. | A pointer to a `Message` with parsed content if a read was successful, otherwise `nullptr`. | 119 | | `void` | `DestroyMessage` | `Message* msg` | Deletes the pointed message null. | None | 120 | | `bool` | `GetPlatformInformation` | None | Requests firmware version and MCU ID from sensor. This method is automatically called as part of `Start()` method and stores values in class members `FirmwareVersionMajor`, `FirmwareVersionMinor`, `MCUUniqueIdentifier`. | `true` if write succeeded, otherwise `false` *. | 121 | 122 | *) On non-Atmel platforms, there will be no error signalled if low level I2C communication fails. This is due to shortcomings in underlying I2C library. 123 | 124 | ## Public Members 125 | 126 | | Type | Name | Description | 127 | | --- | --- | --- | 128 | | `uint8_t` | `FirmwareVersionMajor` | Sensor firmware version, major. Set when sensor is initialized by calling the `Start()` method. | 129 | | `uint8_t` | `FirmwareVersionMinor` | Sensor firmware version, minor. Set when sensor is initialized by calling the `Start()` method. | 130 | | `char*` | `MCUUniqueIdentifier` | Pointer to string holding the sensor's MCU ID. Set when sensor is initialized by calling the `Start()` method. | 131 | 132 | 133 | # Release notes 134 | 135 | 136 | ## zForce Arduino Library 1.8.0 137 | 138 | 139 | ### Contents of Release 140 | - Neonode zForce Library version 1.8.0 in source form at Neonode's GitHub public repository. 141 | 142 | ### Main Features 143 | 144 | - Added configurable I2C address to support touch sensors configured with non-default I2C address. 145 | - New functionality for sending and receiving raw ASN.1 messages: 146 | - method: SendRawMessage 147 | - method: ReceiveRawMessage 148 | - Added functionality for reading sensor ID: 149 | - method: GetPlatformInformation 150 | - member: FirmwareVersionMajor 151 | - member: FirmwareVersionMinor 152 | - member: MCUUniqueIdentifier 153 | - Support for ASN.1 messages up to 255 bytes (was 127 bytes in earlier versions). 154 | - Enable method now also sets the sensor in digitizer mode before enabling. 155 | 156 | ### Bugfixes 157 | 158 | - Touch notification timestamp fixed. 159 | - Corrections to Enable method. 160 | - Corrections to Frequency method. 161 | - Corrections to TouchMode method. 162 | - Corrections to FloatingProtection method. 163 | - Corrections to TouchFormat method. 164 | 165 | ### Known Issues 166 | - None. 167 | -------------------------------------------------------------------------------- /example/zForceKeyboardMouse/zForceKeyboardMouse.ino: -------------------------------------------------------------------------------- 1 | /* Neonode zForce v7 interface library for Arduino 2 | 3 | This example code is distributed freely. 4 | This is an exception from the rest of the library that is released 5 | under GNU Lesser General Public License. 6 | 7 | The purpose of this example code is to demonstrate parts of the 8 | library's functionality and capabilities. It is free to use, copy 9 | and edit without restrictions. 10 | 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | // IMPORTANT: change "1" to assigned GPIO digital pin for dataReady signal in your setup: 18 | #define DATA_READY 1 19 | 20 | long globalMillis = millis(); // global timestamp 21 | const int keyboardBoundary = 750; // set boundary between mouse and keyboard areas on the x-axis to 75 mm. 22 | const int holdTime = 150; // sensitivity for mouse "left-click", unit in milli-second 23 | 24 | TouchData previousTouch; 25 | 26 | void setup() 27 | { 28 | Keyboard.begin(); // initialize keyboard 29 | Mouse.begin(); // initialize mouse 30 | Serial.begin(115200); 31 | Serial.println("zforce start"); 32 | zforce.Start(DATA_READY); 33 | 34 | Message* msg = nullptr; 35 | 36 | zforce.Enable(true); 37 | 38 | msg = zforce.GetMessage(); 39 | 40 | do 41 | { 42 | msg = zforce.GetMessage(); 43 | } while (msg == nullptr); 44 | 45 | if (msg->type == MessageType::ENABLETYPE) 46 | { 47 | Serial.print("Message type is: "); 48 | Serial.println((int)msg->type); 49 | Serial.println("Sensor is now enabled and will report touches."); 50 | } 51 | 52 | zforce.DestroyMessage(msg); 53 | } 54 | 55 | void loop() 56 | { 57 | Message* touch = zforce.GetMessage(); 58 | 59 | if (touch != nullptr) 60 | { 61 | if (touch->type == MessageType::TOUCHTYPE) 62 | { 63 | //Sends reported touch coordinates [x,y] and event data to mouse and keyboard function. 64 | loopMouse(((TouchMessage*)touch)->touchData[0].x, ((TouchMessage*)touch)->touchData[0].y, ((TouchMessage*)touch)->touchData[0].event); 65 | loopKeyboard(((TouchMessage*)touch)->touchData[0].x, ((TouchMessage*)touch)->touchData[0].y, ((TouchMessage*)touch)->touchData[0].event); 66 | } 67 | zforce.DestroyMessage(touch); 68 | } 69 | } 70 | 71 | void loopMouse(int16_t x , int16_t y, int8_t event) 72 | { 73 | if (x <= keyboardBoundary) //return if the touch object is outside mouse area 74 | { 75 | return; 76 | } 77 | 78 | switch (event) 79 | { 80 | case 0: // DOWN event 81 | previousTouch.x = x; 82 | previousTouch.y = y; 83 | globalMillis = millis(); 84 | Serial.println("Mouse Input - DOWN"); 85 | break; 86 | 87 | case 1: // MOVE event 88 | if ((millis() - globalMillis) >= holdTime) 89 | { 90 | Mouse.move((x - previousTouch.x), (y - previousTouch.y)); 91 | Serial.println("Mouse Input - Moving cursor"); 92 | } 93 | previousTouch.x = x; 94 | previousTouch.y = y; 95 | break; 96 | 97 | case 2: // UP event 98 | Serial.print("Mouse Input - UP"); 99 | if (millis() - globalMillis < holdTime) // mouse "left click", sensitivity 100 | { // can be tuned by changing "holdTime" 101 | Mouse.click(MOUSE_LEFT); 102 | Serial.print("(Left-Click)"); 103 | } 104 | Serial.println(""); 105 | break; 106 | 107 | default: 108 | break; 109 | } 110 | } 111 | 112 | void loopKeyboard(int16_t x , int16_t y, int8_t event) 113 | { 114 | if (x > keyboardBoundary) //return if the touch object is outside keyboard area 115 | { 116 | return; 117 | } 118 | 119 | // Only act on event == DOWN, i.e. when an object has entered the touch area 120 | if (event == 0) 121 | { 122 | //assign Key to the given interval 123 | if (y < 250) 124 | { 125 | Keyboard.print('A'); //Print Key "A" 126 | Serial.println("Keyboard Input - Button Press 'A'"); 127 | } 128 | else if (y < 500) 129 | { 130 | Keyboard.print('B'); //Print Key "B" 131 | Serial.println("Keyboard Input - Button Press 'B'"); 132 | } 133 | else if (y < 750) 134 | { 135 | Keyboard.print('C'); //Print Key "C" 136 | Serial.println("Keyboard Input - Button Press 'C'"); 137 | } 138 | else if (y < 1000) 139 | { 140 | Keyboard.print('D'); //Print Key "D" 141 | Serial.println("Keyboard Input - Button Press 'D'"); 142 | } 143 | else 144 | { 145 | Keyboard.print('E'); //Print Key "E" 146 | Serial.println("Keyboard Input - Button Press 'E'"); 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /example/zForceLibraryExampleCode/zForceLibraryExampleCode.ino: -------------------------------------------------------------------------------- 1 | /* Neonode zForce v7 interface library for Arduino 2 | 3 | This example code is distributed freely. 4 | This is an exception from the rest of the library that is released 5 | under GNU Lesser General Public License. 6 | 7 | The purpose of this example code is to demonstrate parts of the 8 | library's functionality and capabilities. It is free to use, copy 9 | and edit without restrictions. 10 | 11 | */ 12 | 13 | #include 14 | 15 | // IMPORTANT: change "1" to assigned GPIO digital pin for dataReady signal in your setup: 16 | #define DATA_READY 1 17 | 18 | void setup() 19 | { 20 | Serial.begin(115200); 21 | while(!Serial){}; 22 | 23 | Serial.println("zforce start"); 24 | zforce.Start(DATA_READY); 25 | init_sensor(); 26 | } 27 | 28 | void loop() 29 | { 30 | // Continuously read any messages available from the sensor and print 31 | // touch data to Serial interface. Normally there should be only 32 | // touch notifications as long as no request messages are sent to the sensor. 33 | Message* touch = zforce.GetMessage(); 34 | if (touch != nullptr) 35 | { 36 | if (touch->type == MessageType::TOUCHTYPE) 37 | { 38 | for (uint8_t i = 0; i < ((TouchMessage*)touch)->touchCount; i++) 39 | { 40 | Serial.print("X is: "); 41 | Serial.println(((TouchMessage*)touch)->touchData[i].x); 42 | Serial.print("Y is: "); 43 | Serial.println(((TouchMessage*)touch)->touchData[i].y); 44 | Serial.print("ID is: "); 45 | Serial.println(((TouchMessage*)touch)->touchData[i].id); 46 | Serial.print("Event is: "); 47 | Serial.println(((TouchMessage*)touch)->touchData[i].event); 48 | } 49 | } 50 | else if (touch->type == MessageType::BOOTCOMPLETETYPE) 51 | { 52 | // If we for some reason receive a boot complete 53 | // message, the sensor needs to be reinitialized: 54 | init_sensor(); 55 | } 56 | 57 | zforce.DestroyMessage(touch); 58 | } 59 | } 60 | 61 | 62 | // Write some configuration parameters to sensor and enable sensor. 63 | // The choice of parameters to configure here are just examples to show how to 64 | // use the library. 65 | // NOTE: Sensor firmware versions 2.xx has persistent storage of configuration 66 | // parameters while versions 1.xx does not. See the library documentation for 67 | // further info. 68 | void init_sensor() 69 | { 70 | Message *msg = nullptr; 71 | 72 | 73 | // Send and read ReverseX 74 | zforce.ReverseX(false); 75 | 76 | do 77 | { 78 | msg = zforce.GetMessage(); 79 | } while (msg == nullptr); 80 | 81 | if (msg->type == MessageType::REVERSEXTYPE) 82 | { 83 | Serial.println("Received ReverseX Response"); 84 | Serial.print("Message type is: "); 85 | Serial.println((int)msg->type); 86 | } 87 | 88 | zforce.DestroyMessage(msg); 89 | 90 | 91 | // Send and read ReverseY 92 | zforce.ReverseY(false); 93 | 94 | do 95 | { 96 | msg = zforce.GetMessage(); 97 | } while (msg == nullptr); 98 | 99 | if (msg->type == MessageType::REVERSEYTYPE) 100 | { 101 | Serial.println("Received ReverseY Response"); 102 | Serial.print("Message type is: "); 103 | Serial.println((int)msg->type); 104 | } 105 | 106 | zforce.DestroyMessage(msg); 107 | 108 | 109 | // Send and read Touch Active Area 110 | zforce.TouchActiveArea(0, 0, 4000, 4000); 111 | 112 | do 113 | { 114 | msg = zforce.GetMessage(); 115 | } while (msg == nullptr); 116 | 117 | if (msg->type == MessageType::TOUCHACTIVEAREATYPE) 118 | { 119 | Serial.print("minX is: "); 120 | Serial.println(((TouchActiveAreaMessage *)msg)->minX); 121 | Serial.print("minY is: "); 122 | Serial.println(((TouchActiveAreaMessage *)msg)->minY); 123 | Serial.print("maxX is: "); 124 | Serial.println(((TouchActiveAreaMessage *)msg)->maxX); 125 | Serial.print("maxY is: "); 126 | Serial.println(((TouchActiveAreaMessage *)msg)->maxY); 127 | } 128 | 129 | zforce.DestroyMessage(msg); 130 | 131 | 132 | // Send and read Enable 133 | 134 | zforce.Enable(true); 135 | 136 | do 137 | { 138 | msg = zforce.GetMessage(); 139 | } while (msg == nullptr); 140 | 141 | if (msg->type == MessageType::ENABLETYPE) 142 | { 143 | Serial.print("Message type is: "); 144 | Serial.println((int)msg->type); 145 | Serial.println("Sensor is now enabled and will report touches."); 146 | } 147 | 148 | zforce.DestroyMessage(msg); 149 | } 150 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Zforce 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | MessageType KEYWORD1 9 | TouchEvent KEYWORD1 10 | TouchData KEYWORD1 11 | Message KEYWORD1 12 | TouchMessage KEYWORD1 13 | EnableMessage KEYWORD1 14 | TouchActiveAreaMessage KEYWORD1 15 | FlipXYMessage KEYWORD1 16 | ReverseXMessage KEYWORD1 17 | ReverseYMessage KEYWORD1 18 | ReportedTouchesMessage KEYWORD1 19 | Zforce KEYWORD1 20 | FrequencyMessage KEYWORD1 21 | TouchModeMessage KEYWORD1 22 | TouchModes KEYWORD1 23 | 24 | ####################################### 25 | # Methods and Functions (KEYWORD2) 26 | ####################################### 27 | 28 | Start KEYWORD2 29 | Read KEYWORD2 30 | Write KEYWORD2 31 | Enable KEYWORD2 32 | TouchActiveArea KEYWORD2 33 | FlipXY KEYWORD2 34 | ReverseX KEYWORD2 35 | ReverseY KEYWORD2 36 | ReportedTouches KEYWORD2 37 | GetDataReady KEYWORD2 38 | GetMessage KEYWORD2 39 | DestroyMessage KEYWORD2 40 | Frequency KEYWORD2 41 | TouchMode KEYWORD2 42 | 43 | ####################################### 44 | # Instances (KEYWORD2) 45 | ####################################### 46 | 47 | zforce KEYWORD2 48 | 49 | ####################################### 50 | # Constants (LITERAL1) 51 | ####################################### -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zForceAirLibrary", 3 | "keywords": "zForce AIR, zForce, neonode, air, touch, sensor", 4 | "description": "A library to communicate with the zForce AIR Sensor over I2C.", 5 | "homepage": "https://support.neonode.com/docs/", 6 | "repository": 7 | { 8 | "type": "git", 9 | "url": "https://github.com/neonode-inc/zforce-arduino" 10 | }, 11 | "version": "1.8.0", 12 | "frameworks": "arduino", 13 | "platforms": "*" 14 | } 15 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=zForce Air Library 2 | version=1.8.0 3 | author=Neonode (support@neonode.com) 4 | maintainer=Neonode (support@neonode.com) 5 | sentence=A library that makes it easy to communicate with the zForce AIR Sensor. 6 | paragraph=Supports most Arduino atmel based platforms. Replace the I2C communication to support other platforms. 7 | category=Communication 8 | url=https://github.com/neonode-inc/zforce-arduino 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/I2C/I2C.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | I2C.cpp - I2C library 3 | Copyright (c) 2011-2012 Wayne Truchsess. All right reserved. 4 | Rev 5.0 - January 24th, 2012 5 | - Removed the use of interrupts completely from the library 6 | so TWI state changes are now polled. 7 | - Added calls to lockup() function in most functions 8 | to combat arbitration problems 9 | - Fixed scan() procedure which left timeouts enabled 10 | and set to 80msec after exiting procedure 11 | - Changed scan() address range back to 0 - 0x7F 12 | - Removed all Wire legacy functions from library 13 | - A big thanks to Richard Baldwin for all the testing 14 | and feedback with debugging bus lockups! 15 | Rev 4.0 - January 14th, 2012 16 | - Updated to make compatible with 8MHz clock frequency 17 | Rev 3.0 - January 9th, 2012 18 | - Modified library to be compatible with Arduino 1.0 19 | - Changed argument type from boolean to uint8_t in pullUp(), 20 | setSpeed() and receiveByte() functions for 1.0 compatability 21 | - Modified return values for timeout feature to report 22 | back where in the transmission the timeout occured. 23 | - added function scan() to perform a bus scan to find devices 24 | attached to the I2C bus. Similar to work done by Todbot 25 | and Nick Gammon 26 | Rev 2.0 - September 19th, 2011 27 | - Added support for timeout function to prevent 28 | and recover from bus lockup (thanks to PaulS 29 | and CrossRoads on the Arduino forum) 30 | - Changed return type for stop() from void to 31 | uint8_t to handle timeOut function 32 | Rev 1.0 - August 8th, 2011 33 | 34 | This is a modified version of the Arduino Wire/TWI 35 | library. Functions were rewritten to provide more functionality 36 | and also the use of Repeated Start. Some I2C devices will not 37 | function correctly without the use of a Repeated Start. The 38 | initial version of this library only supports the Master. 39 | 40 | 41 | This library is free software; you can redistribute it and/or 42 | modify it under the terms of the GNU Lesser General Public 43 | License as published by the Free Software Foundation; either 44 | version 2.1 of the License, or (at your option) any later version. 45 | 46 | This library is distributed in the hope that it will be useful, 47 | but WITHOUT ANY WARRANTY; without even the implied warranty of 48 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 49 | Lesser General Public License for more details. 50 | 51 | You should have received a copy of the GNU Lesser General Public 52 | License along with this library; if not, write to the Free Software 53 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 54 | */ 55 | 56 | 57 | 58 | #if(ARDUINO >= 100) 59 | #include 60 | #else 61 | #include 62 | #endif 63 | 64 | #include 65 | 66 | #include "I2C.h" 67 | 68 | #if USE_I2C_LIB 69 | 70 | uint8_t I2C::bytesAvailable = 0; 71 | uint8_t I2C::bufferIndex = 0; 72 | uint8_t I2C::totalBytes = 0; 73 | uint16_t I2C::timeOutDelay = 0; 74 | 75 | I2C::I2C() 76 | { 77 | } 78 | 79 | 80 | ////////////// Public Methods //////////////////////////////////////// 81 | 82 | 83 | 84 | void I2C::begin() 85 | { 86 | #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__) 87 | // activate internal pull-ups for twi 88 | // as per note from atmega8 manual pg167 89 | sbi(PORTC, 4); 90 | sbi(PORTC, 5); 91 | #else 92 | // activate internal pull-ups for twi 93 | // as per note from atmega128 manual pg204 94 | sbi(PORTD, 0); 95 | sbi(PORTD, 1); 96 | #endif 97 | // initialize twi prescaler and bit rate 98 | cbi(TWSR, TWPS0); 99 | cbi(TWSR, TWPS1); 100 | TWBR = ((F_CPU / 100000) - 16) / 2; 101 | // enable twi module and acks 102 | TWCR = _BV(TWEN) | _BV(TWEA); 103 | } 104 | 105 | void I2C::end() 106 | { 107 | TWCR = 0; 108 | } 109 | 110 | void I2C::timeOut(uint16_t _timeOut) 111 | { 112 | timeOutDelay = _timeOut; 113 | } 114 | 115 | void I2C::setSpeed(uint8_t _fast) 116 | { 117 | if(!_fast) 118 | { 119 | TWBR = ((F_CPU / 100000) - 16) / 2; 120 | } 121 | else 122 | { 123 | TWBR = ((F_CPU / 400000) - 16) / 2; 124 | } 125 | } 126 | 127 | void I2C::pullup(uint8_t activate) 128 | { 129 | if(activate) 130 | { 131 | #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__) 132 | // activate internal pull-ups for twi 133 | // as per note from atmega8 manual pg167 134 | sbi(PORTC, 4); 135 | sbi(PORTC, 5); 136 | #else 137 | // activate internal pull-ups for twi 138 | // as per note from atmega128 manual pg204 139 | sbi(PORTD, 0); 140 | sbi(PORTD, 1); 141 | #endif 142 | } 143 | else 144 | { 145 | #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__) 146 | // deactivate internal pull-ups for twi 147 | // as per note from atmega8 manual pg167 148 | cbi(PORTC, 4); 149 | cbi(PORTC, 5); 150 | #else 151 | // deactivate internal pull-ups for twi 152 | // as per note from atmega128 manual pg204 153 | cbi(PORTD, 0); 154 | cbi(PORTD, 1); 155 | #endif 156 | } 157 | } 158 | 159 | void I2C::scan() 160 | { 161 | uint16_t tempTime = timeOutDelay; 162 | timeOut(80); 163 | uint8_t totalDevicesFound = 0; 164 | Serial.println("Scanning for devices...please wait"); 165 | Serial.println(); 166 | for(uint8_t s = 0; s <= 0x7F; s++) 167 | { 168 | returnStatus = 0; 169 | returnStatus = start(); 170 | if(!returnStatus) 171 | { 172 | returnStatus = sendAddress(SLA_W(s)); 173 | } 174 | if(returnStatus) 175 | { 176 | if(returnStatus == 1) 177 | { 178 | Serial.println("There is a problem with the bus, could not complete scan"); 179 | timeOutDelay = tempTime; 180 | return; 181 | } 182 | } 183 | else 184 | { 185 | Serial.print("Found device at address - "); 186 | Serial.print(" 0x"); 187 | Serial.println(s,HEX); 188 | totalDevicesFound++; 189 | } 190 | stop(); 191 | } 192 | if(!totalDevicesFound){Serial.println("No devices found");} 193 | timeOutDelay = tempTime; 194 | } 195 | 196 | 197 | uint8_t I2C::available() 198 | { 199 | return(bytesAvailable); 200 | } 201 | 202 | uint8_t I2C::receive() 203 | { 204 | bufferIndex = totalBytes - bytesAvailable; 205 | if(!bytesAvailable) 206 | { 207 | bufferIndex = 0; 208 | return(0); 209 | } 210 | bytesAvailable--; 211 | return(data[bufferIndex]); 212 | } 213 | 214 | 215 | /*return values for new functions that use the timeOut feature 216 | will now return at what point in the transmission the timeout 217 | occurred. Looking at a full communication sequence between a 218 | master and slave (transmit data and then readback data) there 219 | a total of 7 points in the sequence where a timeout can occur. 220 | These are listed below and correspond to the returned value: 221 | 1 - Waiting for successful completion of a Start bit 222 | 2 - Waiting for ACK/NACK while addressing slave in transmit mode (MT) 223 | 3 - Waiting for ACK/NACK while sending data to the slave 224 | 4 - Waiting for successful completion of a Repeated Start 225 | 5 - Waiting for ACK/NACK while addressing slave in receiver mode (MR) 226 | 6 - Waiting for ACK/NACK while receiving data from the slave 227 | 7 - Waiting for successful completion of the Stop bit 228 | 229 | All possible return values: 230 | 0 Function executed with no errors 231 | 1 - 7 Timeout occurred, see above list 232 | 8 - 0xFF See datasheet for exact meaning */ 233 | 234 | 235 | ///////////////////////////////////////////////////// 236 | 237 | uint8_t I2C::write(uint8_t address, uint8_t registerAddress) 238 | { 239 | returnStatus = 0; 240 | returnStatus = start(); 241 | if(returnStatus){return(returnStatus);} 242 | returnStatus = sendAddress(SLA_W(address)); 243 | if(returnStatus) 244 | { 245 | if(returnStatus == 1){return(2);} 246 | return(returnStatus); 247 | } 248 | returnStatus = sendByte(registerAddress); 249 | if(returnStatus) 250 | { 251 | if(returnStatus == 1){return(3);} 252 | return(returnStatus); 253 | } 254 | returnStatus = stop(); 255 | if(returnStatus) 256 | { 257 | if(returnStatus == 1){return(7);} 258 | return(returnStatus); 259 | } 260 | return(returnStatus); 261 | } 262 | 263 | uint8_t I2C::write(int address, int registerAddress) 264 | { 265 | return(write((uint8_t) address, (uint8_t) registerAddress)); 266 | } 267 | 268 | uint8_t I2C::write(uint8_t address, uint8_t registerAddress, uint8_t data) 269 | { 270 | returnStatus = 0; 271 | returnStatus = start(); 272 | if(returnStatus){return(returnStatus);} 273 | returnStatus = sendAddress(SLA_W(address)); 274 | if(returnStatus) 275 | { 276 | if(returnStatus == 1){return(2);} 277 | return(returnStatus); 278 | } 279 | returnStatus = sendByte(registerAddress); 280 | if(returnStatus) 281 | { 282 | if(returnStatus == 1){return(3);} 283 | return(returnStatus); 284 | } 285 | returnStatus = sendByte(data); 286 | if(returnStatus) 287 | { 288 | if(returnStatus == 1){return(3);} 289 | return(returnStatus); 290 | } 291 | returnStatus = stop(); 292 | if(returnStatus) 293 | { 294 | if(returnStatus == 1){return(7);} 295 | return(returnStatus); 296 | } 297 | return(returnStatus); 298 | } 299 | 300 | uint8_t I2C::write(int address, int registerAddress, int data) 301 | { 302 | return(write((uint8_t) address, (uint8_t) registerAddress, (uint8_t) data)); 303 | } 304 | 305 | uint8_t I2C::write(uint8_t address, uint8_t registerAddress, char *data) 306 | { 307 | uint8_t bufferLength = strlen(data); 308 | returnStatus = 0; 309 | returnStatus = write(address, registerAddress, (uint8_t*)data, bufferLength); 310 | return(returnStatus); 311 | } 312 | 313 | uint8_t I2C::write(uint8_t address, uint8_t registerAddress, uint8_t *data, uint8_t numberBytes) 314 | { 315 | returnStatus = 0; 316 | returnStatus = start(); 317 | if(returnStatus){return(returnStatus);} 318 | returnStatus = sendAddress(SLA_W(address)); 319 | if(returnStatus) 320 | { 321 | if(returnStatus == 1){return(2);} 322 | return(returnStatus); 323 | } 324 | returnStatus = sendByte(registerAddress); 325 | if(returnStatus) 326 | { 327 | if(returnStatus == 1){return(3);} 328 | return(returnStatus); 329 | } 330 | for (uint8_t i = 0; i < numberBytes; i++) 331 | { 332 | returnStatus = sendByte(data[i]); 333 | if(returnStatus) 334 | { 335 | if(returnStatus == 1){return(3);} 336 | return(returnStatus); 337 | } 338 | } 339 | returnStatus = stop(); 340 | if(returnStatus) 341 | { 342 | if(returnStatus == 1){return(7);} 343 | return(returnStatus); 344 | } 345 | return(returnStatus); 346 | } 347 | 348 | uint8_t I2C::read(int address, int numberBytes) 349 | { 350 | return(read((uint8_t) address, (uint8_t) numberBytes)); 351 | } 352 | 353 | uint8_t I2C::read(uint8_t address, uint8_t numberBytes) 354 | { 355 | bytesAvailable = 0; 356 | bufferIndex = 0; 357 | if(numberBytes == 0){numberBytes++;} 358 | nack = numberBytes - 1; 359 | returnStatus = 0; 360 | returnStatus = start(); 361 | if(returnStatus){return(returnStatus);} 362 | returnStatus = sendAddress(SLA_R(address)); 363 | if(returnStatus) 364 | { 365 | if(returnStatus == 1){return(5);} 366 | return(returnStatus); 367 | } 368 | for(uint8_t i = 0; i < numberBytes; i++) 369 | { 370 | if( i == nack ) 371 | { 372 | returnStatus = receiveByte(0); 373 | if(returnStatus == 1){return(6);} 374 | 375 | if(returnStatus != MR_DATA_NACK){return(returnStatus);} 376 | } 377 | else 378 | { 379 | returnStatus = receiveByte(1); 380 | if(returnStatus == 1){return(6);} 381 | if(returnStatus != MR_DATA_ACK){return(returnStatus);} 382 | } 383 | data[i] = TWDR; 384 | bytesAvailable = i+1; 385 | totalBytes = i+1; 386 | } 387 | returnStatus = stop(); 388 | if(returnStatus) 389 | { 390 | if(returnStatus == 1){return(7);} 391 | return(returnStatus); 392 | } 393 | return(returnStatus); 394 | } 395 | 396 | uint8_t I2C::read(int address, int registerAddress, int numberBytes) 397 | { 398 | return(read((uint8_t) address, (uint8_t) registerAddress, (uint8_t) numberBytes)); 399 | } 400 | 401 | uint8_t I2C::read(uint8_t address, uint8_t registerAddress, uint8_t numberBytes) 402 | { 403 | bytesAvailable = 0; 404 | bufferIndex = 0; 405 | if(numberBytes == 0){numberBytes++;} 406 | nack = numberBytes - 1; 407 | returnStatus = 0; 408 | returnStatus = start(); 409 | if(returnStatus){return(returnStatus);} 410 | returnStatus = sendAddress(SLA_W(address)); 411 | if(returnStatus) 412 | { 413 | if(returnStatus == 1){return(2);} 414 | return(returnStatus); 415 | } 416 | returnStatus = sendByte(registerAddress); 417 | if(returnStatus) 418 | { 419 | if(returnStatus == 1){return(3);} 420 | return(returnStatus); 421 | } 422 | returnStatus = start(); 423 | if(returnStatus) 424 | { 425 | if(returnStatus == 1){return(4);} 426 | return(returnStatus); 427 | } 428 | returnStatus = sendAddress(SLA_R(address)); 429 | if(returnStatus) 430 | { 431 | if(returnStatus == 1){return(5);} 432 | return(returnStatus); 433 | } 434 | for(uint8_t i = 0; i < numberBytes; i++) 435 | { 436 | if( i == nack ) 437 | { 438 | returnStatus = receiveByte(0); 439 | if(returnStatus == 1){return(6);} 440 | if(returnStatus != MR_DATA_NACK){return(returnStatus);} 441 | } 442 | else 443 | { 444 | returnStatus = receiveByte(1); 445 | if(returnStatus == 1){return(6);} 446 | if(returnStatus != MR_DATA_ACK){return(returnStatus);} 447 | } 448 | data[i] = TWDR; 449 | bytesAvailable = i+1; 450 | totalBytes = i+1; 451 | } 452 | returnStatus = stop(); 453 | if(returnStatus) 454 | { 455 | if(returnStatus == 1){return(7);} 456 | return(returnStatus); 457 | } 458 | return(returnStatus); 459 | } 460 | 461 | uint8_t I2C::read(uint8_t address, uint8_t numberBytes, uint8_t *dataBuffer) 462 | { 463 | bytesAvailable = 0; 464 | bufferIndex = 0; 465 | if(numberBytes == 0){numberBytes++;} 466 | nack = numberBytes - 1; 467 | returnStatus = 0; 468 | returnStatus = start(); 469 | if(returnStatus){return(returnStatus);} 470 | returnStatus = sendAddress(SLA_R(address)); 471 | if(returnStatus) 472 | { 473 | if(returnStatus == 1){return(5);} 474 | return(returnStatus); 475 | } 476 | for(uint8_t i = 0; i < numberBytes; i++) 477 | { 478 | if( i == nack ) 479 | { 480 | returnStatus = receiveByte(0); 481 | if(returnStatus == 1){return(6);} 482 | if(returnStatus != MR_DATA_NACK){return(returnStatus);} 483 | } 484 | else 485 | { 486 | returnStatus = receiveByte(1); 487 | if(returnStatus == 1){return(6);} 488 | if(returnStatus != MR_DATA_ACK){return(returnStatus);} 489 | } 490 | dataBuffer[i] = TWDR; 491 | bytesAvailable = i+1; 492 | totalBytes = i+1; 493 | } 494 | returnStatus = stop(); 495 | if(returnStatus) 496 | { 497 | if(returnStatus == 1){return(7);} 498 | return(returnStatus); 499 | } 500 | return(returnStatus); 501 | } 502 | 503 | uint8_t I2C::read(uint8_t address, uint8_t registerAddress, uint8_t numberBytes, uint8_t *dataBuffer) 504 | { 505 | bytesAvailable = 0; 506 | bufferIndex = 0; 507 | if(numberBytes == 0){numberBytes++;} 508 | nack = numberBytes - 1; 509 | returnStatus = 0; 510 | returnStatus = start(); 511 | if(returnStatus){return(returnStatus);} 512 | returnStatus = sendAddress(SLA_W(address)); 513 | if(returnStatus) 514 | { 515 | if(returnStatus == 1){return(2);} 516 | return(returnStatus); 517 | } 518 | returnStatus = sendByte(registerAddress); 519 | if(returnStatus) 520 | { 521 | if(returnStatus == 1){return(3);} 522 | return(returnStatus); 523 | } 524 | returnStatus = start(); 525 | if(returnStatus) 526 | { 527 | if(returnStatus == 1){return(4);} 528 | return(returnStatus); 529 | } 530 | returnStatus = sendAddress(SLA_R(address)); 531 | if(returnStatus) 532 | { 533 | if(returnStatus == 1){return(5);} 534 | return(returnStatus); 535 | } 536 | for(uint8_t i = 0; i < numberBytes; i++) 537 | { 538 | if( i == nack ) 539 | { 540 | returnStatus = receiveByte(0); 541 | if(returnStatus == 1){return(6);} 542 | if(returnStatus != MR_DATA_NACK){return(returnStatus);} 543 | } 544 | else 545 | { 546 | returnStatus = receiveByte(1); 547 | if(returnStatus == 1){return(6);} 548 | if(returnStatus != MR_DATA_ACK){return(returnStatus);} 549 | } 550 | dataBuffer[i] = TWDR; 551 | bytesAvailable = i+1; 552 | totalBytes = i+1; 553 | } 554 | returnStatus = stop(); 555 | if(returnStatus) 556 | { 557 | if(returnStatus == 1){return(7);} 558 | return(returnStatus); 559 | } 560 | return(returnStatus); 561 | } 562 | 563 | 564 | /////////////// Private Methods //////////////////////////////////////// 565 | 566 | 567 | uint8_t I2C::start() 568 | { 569 | unsigned long startingTime = millis(); 570 | TWCR = (1<= timeOutDelay) 575 | { 576 | lockUp(); 577 | return(1); 578 | } 579 | 580 | } 581 | if ((TWI_STATUS == START) || (TWI_STATUS == REPEATED_START)) 582 | { 583 | return(0); 584 | } 585 | if (TWI_STATUS == LOST_ARBTRTN) 586 | { 587 | uint8_t bufferedStatus = TWI_STATUS; 588 | lockUp(); 589 | return(bufferedStatus); 590 | } 591 | return(TWI_STATUS); 592 | } 593 | 594 | uint8_t I2C::sendAddress(uint8_t i2cAddress) 595 | { 596 | TWDR = i2cAddress; 597 | unsigned long startingTime = millis(); 598 | TWCR = (1<= timeOutDelay) 603 | { 604 | lockUp(); 605 | return(1); 606 | } 607 | 608 | } 609 | if ((TWI_STATUS == MT_SLA_ACK) || (TWI_STATUS == MR_SLA_ACK)) 610 | { 611 | return(0); 612 | } 613 | uint8_t bufferedStatus = TWI_STATUS; 614 | if ((TWI_STATUS == MT_SLA_NACK) || (TWI_STATUS == MR_SLA_NACK)) 615 | { 616 | stop(); 617 | return(bufferedStatus); 618 | } 619 | else 620 | { 621 | lockUp(); 622 | return(bufferedStatus); 623 | } 624 | } 625 | 626 | uint8_t I2C::sendByte(uint8_t i2cData) 627 | { 628 | TWDR = i2cData; 629 | unsigned long startingTime = millis(); 630 | TWCR = (1<= timeOutDelay) 635 | { 636 | lockUp(); 637 | return(1); 638 | } 639 | 640 | } 641 | if (TWI_STATUS == MT_DATA_ACK) 642 | { 643 | return(0); 644 | } 645 | uint8_t bufferedStatus = TWI_STATUS; 646 | if (TWI_STATUS == MT_DATA_NACK) 647 | { 648 | stop(); 649 | return(bufferedStatus); 650 | } 651 | else 652 | { 653 | lockUp(); 654 | return(bufferedStatus); 655 | } 656 | } 657 | 658 | uint8_t I2C::receiveByte(uint8_t ack) 659 | { 660 | unsigned long startingTime = millis(); 661 | if(ack) 662 | { 663 | TWCR = (1<= timeOutDelay) 674 | { 675 | lockUp(); 676 | return(1); 677 | } 678 | } 679 | if (TWI_STATUS == LOST_ARBTRTN) 680 | { 681 | uint8_t bufferedStatus = TWI_STATUS; 682 | lockUp(); 683 | return(bufferedStatus); 684 | } 685 | return(TWI_STATUS); 686 | } 687 | 688 | uint8_t I2C::stop() 689 | { 690 | unsigned long startingTime = millis(); 691 | TWCR = (1<= timeOutDelay) 696 | { 697 | lockUp(); 698 | return(1); 699 | } 700 | 701 | } 702 | return(0); 703 | } 704 | 705 | void I2C::lockUp() 706 | { 707 | TWCR = 0; //releases SDA and SCL lines to high impedance 708 | TWCR = _BV(TWEN) | _BV(TWEA); //reinitialize TWI 709 | } 710 | 711 | I2C I2c = I2C(); 712 | 713 | #endif // endif USE_I2C_LIB 714 | -------------------------------------------------------------------------------- /src/I2C/I2C.h: -------------------------------------------------------------------------------- 1 | /* 2 | I2C.h - I2C library 3 | Copyright (c) 2011-2012 Wayne Truchsess. All right reserved. 4 | Rev 5.0 - January 24th, 2012 5 | - Removed the use of interrupts completely from the library 6 | so TWI state changes are now polled. 7 | - Added calls to lockup() function in most functions 8 | to combat arbitration problems 9 | - Fixed scan() procedure which left timeouts enabled 10 | and set to 80msec after exiting procedure 11 | - Changed scan() address range back to 0 - 0x7F 12 | - Removed all Wire legacy functions from library 13 | - A big thanks to Richard Baldwin for all the testing 14 | and feedback with debugging bus lockups! 15 | Rev 4.0 - January 14th, 2012 16 | - Updated to make compatible with 8MHz clock frequency 17 | Rev 3.0 - January 9th, 2012 18 | - Modified library to be compatible with Arduino 1.0 19 | - Changed argument type from boolean to uint8_t in pullUp(), 20 | setSpeed() and receiveByte() functions for 1.0 compatability 21 | - Modified return values for timeout feature to report 22 | back where in the transmission the timeout occured. 23 | - added function scan() to perform a bus scan to find devices 24 | attached to the I2C bus. Similar to work done by Todbot 25 | and Nick Gammon 26 | Rev 2.0 - September 19th, 2011 27 | - Added support for timeout function to prevent 28 | and recover from bus lockup (thanks to PaulS 29 | and CrossRoads on the Arduino forum) 30 | - Changed return type for stop() from void to 31 | uint8_t to handle timeOut function 32 | Rev 1.0 - August 8th, 2011 33 | 34 | This is a modified version of the Arduino Wire/TWI 35 | library. Functions were rewritten to provide more functionality 36 | and also the use of Repeated Start. Some I2C devices will not 37 | function correctly without the use of a Repeated Start. The 38 | initial version of this library only supports the Master. 39 | 40 | 41 | This library is free software; you can redistribute it and/or 42 | modify it under the terms of the GNU Lesser General Public 43 | License as published by the Free Software Foundation; either 44 | version 2.1 of the License, or (at your option) any later version. 45 | 46 | This library is distributed in the hope that it will be useful, 47 | but WITHOUT ANY WARRANTY; without even the implied warranty of 48 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 49 | Lesser General Public License for more details. 50 | 51 | You should have received a copy of the GNU Lesser General Public 52 | License along with this library; if not, write to the Free Software 53 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 54 | */ 55 | 56 | 57 | #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega128__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) 58 | #define USE_I2C_LIB 1 59 | #else 60 | #define USE_I2C_LIB 0 61 | #endif 62 | 63 | #if USE_I2C_LIB 64 | 65 | #if(ARDUINO >= 100) 66 | #include 67 | #else 68 | #include 69 | #endif 70 | 71 | #include 72 | 73 | #ifndef I2C_h 74 | #define I2C_h 75 | 76 | 77 | #define START 0x08 78 | #define REPEATED_START 0x10 79 | #define MT_SLA_ACK 0x18 80 | #define MT_SLA_NACK 0x20 81 | #define MT_DATA_ACK 0x28 82 | #define MT_DATA_NACK 0x30 83 | #define MR_SLA_ACK 0x40 84 | #define MR_SLA_NACK 0x48 85 | #define MR_DATA_ACK 0x50 86 | #define MR_DATA_NACK 0x58 87 | #define LOST_ARBTRTN 0x38 88 | #define TWI_STATUS (TWSR & 0xF8) 89 | #define SLA_W(address) (address << 1) 90 | #define SLA_R(address) ((address << 1) + 0x01) 91 | #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) 92 | #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) 93 | 94 | #define MAX_BUFFER_SIZE 32 95 | 96 | 97 | 98 | 99 | class I2C 100 | { 101 | public: 102 | I2C(); 103 | void begin(); 104 | void end(); 105 | void timeOut(uint16_t); 106 | void setSpeed(uint8_t); 107 | void pullup(uint8_t); 108 | void scan(); 109 | uint8_t available(); 110 | uint8_t receive(); 111 | uint8_t write(uint8_t, uint8_t); 112 | uint8_t write(int, int); 113 | uint8_t write(uint8_t, uint8_t, uint8_t); 114 | uint8_t write(int, int, int); 115 | uint8_t write(uint8_t, uint8_t, char*); 116 | uint8_t write(uint8_t, uint8_t, uint8_t*, uint8_t); 117 | uint8_t read(uint8_t, uint8_t); 118 | uint8_t read(int, int); 119 | uint8_t read(uint8_t, uint8_t, uint8_t); 120 | uint8_t read(int, int, int); 121 | uint8_t read(uint8_t, uint8_t, uint8_t*); 122 | uint8_t read(uint8_t, uint8_t, uint8_t, uint8_t*); 123 | 124 | 125 | private: 126 | uint8_t start(); 127 | uint8_t sendAddress(uint8_t); 128 | uint8_t sendByte(uint8_t); 129 | uint8_t receiveByte(uint8_t); 130 | uint8_t stop(); 131 | void lockUp(); 132 | uint8_t returnStatus; 133 | uint8_t nack; 134 | uint8_t data[MAX_BUFFER_SIZE]; 135 | static uint8_t bytesAvailable; 136 | static uint8_t bufferIndex; 137 | static uint8_t totalBytes; 138 | static uint16_t timeOutDelay; 139 | 140 | }; 141 | 142 | extern I2C I2c; 143 | 144 | #endif 145 | #endif 146 | -------------------------------------------------------------------------------- /src/Zforce.cpp: -------------------------------------------------------------------------------- 1 | /* Neonode zForce v7 interface library for Arduino 2 | 3 | Copyright (C) 2019-2023 Neonode Inc. 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | 21 | #include 22 | #include 23 | #include "I2C/I2C.h" 24 | #include "Zforce.h" 25 | #if USE_I2C_LIB == 0 26 | #include 27 | #if(ARDUINO >= 100) 28 | #include 29 | #else 30 | #include 31 | #endif 32 | #endif 33 | 34 | Zforce::Zforce() 35 | { 36 | this->remainingRawLength = 0; 37 | } 38 | 39 | void Zforce::Start(int dr) 40 | { 41 | Start(dr, ZFORCE_DEFAULT_I2C_ADDRESS); 42 | } 43 | 44 | void Zforce::Start(int dr, int i2cAddress) 45 | { 46 | this->i2cAddress = i2cAddress; 47 | dataReady = dr; 48 | pinMode(dataReady, INPUT); 49 | #if USE_I2C_LIB == 1 50 | I2c.setSpeed(1); 51 | I2c.begin(); 52 | #else 53 | Wire.begin(); 54 | #endif 55 | 56 | /* Reading of boot complete and sending/reading of touchformat 57 | * can be moved to user side but is by default 58 | * kept here for simplicity for the end user. */ 59 | 60 | // Read out potential boot complete notification 61 | auto msg = this->GetMessage(); 62 | if (msg != nullptr) 63 | { 64 | this->DestroyMessage(msg); 65 | } 66 | 67 | // Get the touch descriptor from the sensor in order to deserialize the touch notifications 68 | bool successfulTouchFormatRequest = this->TouchFormat(); 69 | 70 | if (successfulTouchFormatRequest) 71 | { 72 | do 73 | { 74 | msg = this->GetMessage(); 75 | } while (msg == nullptr); 76 | 77 | if (msg->type == MessageType::TOUCHFORMATTYPE && touchMetaInformation.touchDescriptor != nullptr) 78 | { 79 | this->touchDescriptorInitialized = true; 80 | } 81 | 82 | this->DestroyMessage(msg); 83 | } 84 | 85 | // Get Platform information 86 | this->GetPlatformInformation(); 87 | do 88 | { 89 | msg = this->GetMessage(); 90 | } while (msg == nullptr); 91 | 92 | if (msg->type == MessageType::PLATFORMINFORMATIONTYPE) 93 | { 94 | this->FirmwareVersionMajor = ((PlatformInformationMessage *)msg)->firmwareVersionMajor; 95 | this->FirmwareVersionMinor = ((PlatformInformationMessage *)msg)->firmwareVersionMinor; 96 | uint8_t length = ((PlatformInformationMessage *)msg)->mcuUniqueIdentifierLength; 97 | this->MCUUniqueIdentifier = new char[length + 1]; 98 | strncpy(this->MCUUniqueIdentifier, ((PlatformInformationMessage *)msg)->mcuUniqueIdentifier, length); 99 | this->MCUUniqueIdentifier[length] = '\0'; 100 | } 101 | this->DestroyMessage(msg); 102 | } 103 | 104 | int Zforce::Read(uint8_t * payload) 105 | { 106 | #if USE_I2C_LIB == 1 107 | int status = 0; 108 | 109 | status = I2c.read(this->i2cAddress, 2); 110 | 111 | // Read the 2 I2C header bytes. 112 | payload[0] = I2c.receive(); 113 | payload[1] = I2c.receive(); 114 | 115 | status = I2c.read(this->i2cAddress, payload[1], &payload[2]); 116 | 117 | return status; // return 0 if success, otherwise error code according to Atmel Data Sheet 118 | #else 119 | Wire.requestFrom(this->i2cAddress, 2); 120 | payload[0] = Wire.read(); 121 | payload[1] = Wire.read(); 122 | 123 | int index = 2; 124 | Wire.requestFrom(this->i2cAddress, payload[1]); 125 | while (Wire.available()) 126 | { 127 | payload[index++] = Wire.read(); 128 | } 129 | 130 | return 0; 131 | #endif 132 | } 133 | 134 | /* 135 | * Sends a message in the form of a byte array. 136 | */ 137 | int Zforce::Write(uint8_t* payload) 138 | { 139 | #if USE_I2C_LIB == 1 140 | int len = payload[1] + 1; 141 | int status = I2c.write(this->i2cAddress, payload[0], &payload[1], len); 142 | 143 | return status; // return 0 if success, otherwise error code according to Atmel Data Sheet 144 | #else 145 | Wire.beginTransmission(this->i2cAddress); 146 | Wire.write(payload, payload[1] + 2); 147 | Wire.endTransmission(); 148 | 149 | return 0; 150 | #endif 151 | } 152 | 153 | /* 154 | * Send the octet array containing an ASN.1 command as is, without validation. 155 | * If you need to send more than 255 bytes, call it more times with new data until done. 156 | * 157 | * payload Pointer to the octet buffer containing the ASN.1 payload to send. 158 | * payloadLength Length of the data to send. 159 | * 160 | * Return Value true for successful send and false if any error occured. 161 | * Sadly, some Arduino i2c drivers do not signal errors. 162 | */ 163 | bool Zforce::SendRawMessage(uint8_t* payload, uint8_t payloadLength) 164 | { 165 | if ((payload == nullptr) || (payloadLength == 0)) 166 | { 167 | return false; 168 | } 169 | 170 | buffer[0] = 0xEE; 171 | buffer[1] = payloadLength; 172 | memcpy(&buffer[2], payload, payloadLength); 173 | 174 | return Write(buffer) == 0; 175 | } 176 | 177 | /* 178 | * Receive a raw ASN.1 message. The only validation done is decoding the initial ASN.1 payload length 179 | * to see if more data should follow. 180 | * 181 | * receivedLength A pointer to where the size of the returned data should be placed. 182 | * remainingLength A pointer to where the size of the remaining data should be placed, if any. 183 | * 184 | * Return value If an error occured (see comment above for the return value of SendRawMessage), 185 | * the pointer will be nullptr. 186 | * A pointer to the ASN.1 payload of the received data is returned. 187 | * Note, that any subsequent read or write-operation, even notifications, touches, etc will 188 | * will overwrite the buffer, so make sure to copy any data you want to save. 189 | */ 190 | uint8_t* Zforce::ReceiveRawMessage(uint8_t* receivedLength, uint16_t *remainingLength) 191 | { 192 | if ((GetDataReady() == HIGH) && !Read(buffer)) 193 | { 194 | uint8_t i2cPayloadLength = buffer[1]; 195 | // Check if this is a second or first call. 196 | if (this->remainingRawLength == 0) 197 | { 198 | // This is the first invocation. 199 | // Check if it's a short, 2, or 3 byte length encoding. 200 | uint8_t firstLengthByte = buffer[3]; 201 | uint16_t asn1AfterHeaderLength; 202 | uint8_t asn1HeaderLength = 2; // EE/EF/F0 + First byte of length. 203 | if (firstLengthByte < 0x80) 204 | { 205 | // Short form. Lower 7 bits are the length, but since the high bit is 0, we don't need to & 0x7F to get the lower 7. 206 | asn1AfterHeaderLength = firstLengthByte; 207 | } 208 | else 209 | { 210 | // Long form. First byte's top bit is set. The lower 7 bits contain the number of length bytes. 211 | // The following 1 or 2 bytes contain the actual length, in Big Endian / Motorola Byte Order encoding. 212 | uint8_t numberOfLengthBytes = (firstLengthByte & 0x7F); 213 | asn1HeaderLength += numberOfLengthBytes; 214 | asn1AfterHeaderLength = buffer[4]; 215 | if (numberOfLengthBytes == 2) 216 | { 217 | asn1AfterHeaderLength <<= 8; 218 | asn1AfterHeaderLength += buffer[5]; 219 | } 220 | else if (numberOfLengthBytes != 1) 221 | { 222 | // The data is most likely corrupted. Valid numbers are 1 and 2. 223 | *receivedLength = 0; 224 | *remainingLength = 0; 225 | return nullptr; 226 | } 227 | } 228 | uint16_t fullAsn1MessageLength = asn1AfterHeaderLength + asn1HeaderLength; 229 | // Full is the full ASN.1 payload, which can be split over several i2c payloads. 230 | this->remainingRawLength = fullAsn1MessageLength - i2cPayloadLength; 231 | *remainingLength = this->remainingRawLength; 232 | *receivedLength = i2cPayloadLength; 233 | } 234 | else 235 | { 236 | // Since this is the second or later invocation, we do NOT parse the ASN.1, since it 237 | // may well be in the middle of some data, and we already know the lengths. 238 | this->remainingRawLength -= i2cPayloadLength; 239 | *remainingLength = this->remainingRawLength; 240 | *receivedLength = i2cPayloadLength; 241 | } 242 | } 243 | else 244 | { 245 | // Either Data Ready was not high, or the read failed. 246 | *remainingLength = 0; 247 | *receivedLength = 0; 248 | return nullptr; 249 | } 250 | 251 | return &buffer[2]; // Skipping the i2c header in the response. 252 | } 253 | 254 | bool Zforce::Enable(bool isEnabled) 255 | { 256 | bool failed = false; 257 | int returnCode; 258 | 259 | uint8_t enable[] = {0xEE, 0x0B, 0xEE, 0x09, 0x40, 0x02, 0x02, 0x00, 0x65, 0x03, 0x81, 0x01, 0x00}; 260 | uint8_t disable[] = {0xEE, 0x0A, 0xEE, 0x08, 0x40, 0x02, 0x02, 0x00, 0x65, 0x02, 0x80, 0x00}; 261 | uint8_t operationMode[] = {0xEE, 0x17, 0xEE, 0x15, 0x40, 0x02, 0x02, 0x00, 0x67, 0x0F, 0x80, 0x01, 0xFF, 0x81, 0x01, 0x00, 0x82, 0x01, 0x00, 0x83, 0x01, 0x00, 0x84, 0x01, 0x00}; 262 | 263 | // We assume that the end user has called GetMessage prior to calling this method 264 | if (isEnabled) 265 | { 266 | returnCode = Write(operationMode); 267 | if (returnCode != 0) 268 | { 269 | failed = true; 270 | } 271 | else 272 | { 273 | Message* msg = nullptr; 274 | do 275 | { 276 | msg = this->GetMessage(); 277 | } while (msg == nullptr); 278 | 279 | this->DestroyMessage(msg); 280 | 281 | returnCode = Write(enable); 282 | } 283 | } 284 | else 285 | { 286 | returnCode = Write(disable); 287 | } 288 | 289 | if (returnCode != 0) 290 | { 291 | failed = true; 292 | } 293 | else 294 | { 295 | lastSentMessage = MessageType::ENABLETYPE; 296 | } 297 | 298 | return !failed; 299 | } 300 | 301 | bool Zforce::GetEnable() 302 | { 303 | bool failed = false; 304 | uint8_t enable[] = {0xEE, 0x08, 0xEE, 0x06, 0x40, 0x02, 0x02, 0x00, 0x65, 0x00}; 305 | 306 | if (Write(enable)) // We assume that the end user has called GetMessage prior to calling this method 307 | { 308 | failed = true; 309 | } 310 | else 311 | { 312 | lastSentMessage = MessageType::ENABLETYPE; 313 | } 314 | 315 | return !failed; 316 | } 317 | 318 | bool Zforce::TouchActiveArea(uint16_t minX, uint16_t minY, uint16_t maxX, uint16_t maxY) 319 | { 320 | bool failed = false; 321 | 322 | uint8_t touchActiveAreaPayloadLength = 2 * 4; 323 | // 2 bytes * 4 entries. Not counting the actual values, which are 1 or 2 bytes. 324 | 325 | // Each value that is >127 gets an extra byte. 326 | 327 | uint8_t minXValue[2]; 328 | uint8_t minYValue[2]; 329 | uint8_t maxXValue[2]; 330 | uint8_t maxYValue[2]; 331 | 332 | uint8_t minXLength = SerializeInt(minX, minXValue); 333 | uint8_t minYLength = SerializeInt(minY, minYValue); 334 | uint8_t maxXLength = SerializeInt(maxX, maxXValue); 335 | uint8_t maxYLength = SerializeInt(maxY, maxYValue); 336 | 337 | touchActiveAreaPayloadLength += minXLength; 338 | touchActiveAreaPayloadLength += minYLength; 339 | touchActiveAreaPayloadLength += maxXLength; 340 | touchActiveAreaPayloadLength += maxYLength; 341 | 342 | #define TAA_ALLHEADERSSIZE (2 + 2 + 4 + 4) 343 | 344 | uint8_t totalLength = TAA_ALLHEADERSSIZE + touchActiveAreaPayloadLength; 345 | // 2 bytes I2C header: 0xEE + 1 byte for Length. Length: totalLength - 2. 346 | // 2 bytes for Request: 0xEE + 1 byte for Length. Length: totalLength - 4. 347 | // 4 bytes for Address: 0x40, 0x02, 0x02, 0x00. 348 | // 4 bytes for touchActiveArea payload headers: 0x73, touchActiveAreaPayloadLength + 2, 0xA2, touchActiveAreaPayloadLength. 349 | // X bytes for the actual payload (calculated above). 350 | 351 | uint8_t touchActiveArea[totalLength] = { 0xEE, (uint8_t)(totalLength - 2), 352 | 0xEE, (uint8_t)(totalLength - 4), 353 | 0x40, 0x02, 0x02, 0x00, 354 | 0x73, (uint8_t)(touchActiveAreaPayloadLength + 2), 0xA2, touchActiveAreaPayloadLength }; 355 | 356 | size_t offset = TAA_ALLHEADERSSIZE; 357 | 358 | // Each value <= 127 is 1 byte, above is 2 bytes. 359 | 360 | // MinX. 361 | touchActiveArea[offset++] = 0x80; // MinX identifier. 362 | touchActiveArea[offset++] = minXLength; 363 | touchActiveArea[offset++] = (uint8_t)minXValue[0]; 364 | if (minXLength == 2) 365 | { 366 | touchActiveArea[offset++] = (uint8_t)minXValue[1]; 367 | } 368 | 369 | // MinY. 370 | touchActiveArea[offset++] = 0x81; // MinY identifier. 371 | touchActiveArea[offset++] = minYLength; 372 | touchActiveArea[offset++] = (uint8_t)minYValue[0]; 373 | if (minYLength == 2) 374 | { 375 | touchActiveArea[offset++] = (uint8_t)minYValue[1]; 376 | } 377 | 378 | // MaxX. 379 | touchActiveArea[offset++] = 0x82; // MaxX identifier. 380 | touchActiveArea[offset++] = maxXLength; 381 | touchActiveArea[offset++] = (uint8_t)maxXValue[0]; 382 | if (maxXLength == 2) 383 | { 384 | touchActiveArea[offset++] = (uint8_t)maxXValue[1]; 385 | } 386 | 387 | // MaxY. 388 | touchActiveArea[offset++] = 0x83; // MaxY identifier. 389 | touchActiveArea[offset++] = maxYLength; 390 | touchActiveArea[offset++] = (uint8_t)maxYValue[0]; 391 | if (maxYLength == 2) 392 | { 393 | touchActiveArea[offset++] = (uint8_t)maxYValue[1]; 394 | } 395 | 396 | if (Write(touchActiveArea)) // We assume that the end user has called GetMessage prior to calling this method 397 | { 398 | failed = true; 399 | } 400 | else 401 | { 402 | lastSentMessage = MessageType::TOUCHACTIVEAREATYPE; 403 | } 404 | 405 | return !failed; 406 | } 407 | 408 | bool Zforce::Frequency(uint16_t idleFrequency, uint16_t fingerFrequency) 409 | { 410 | bool failed = false; 411 | 412 | uint8_t frequencyPayloadLength = 2 * 2; 413 | // 2 bytes * 2 entries. Not counting the actual values, which are 1 or 2 bytes. 414 | 415 | // Each value that is >127 gets an extra byte. 416 | 417 | uint8_t fingerFrequencyValue[2]; 418 | uint8_t idleFrequencyValue[2]; 419 | 420 | uint8_t fingerFrequencyLength = SerializeInt(fingerFrequency, fingerFrequencyValue); 421 | uint8_t idleFrequencyLength = SerializeInt(idleFrequency, idleFrequencyValue); 422 | 423 | frequencyPayloadLength += fingerFrequencyLength; 424 | frequencyPayloadLength += idleFrequencyLength; 425 | 426 | #define FREQ_ALLHEADERSSIZE (2 + 2 + 4 + 2) 427 | 428 | uint8_t totalLength = FREQ_ALLHEADERSSIZE + frequencyPayloadLength; 429 | // 2 bytes I2C header: 0xEE + 1 byte for Length. Length: totalLength - 2. 430 | // 2 bytes for Request: 0xEE + 1 byte for Length. Length: totalLength - 4. 431 | // 4 bytes for Address: 0x40, 0x02, 0x02, 0x00. 432 | // 2 bytes for Frequency payload headers: 0x68, frequencyPayloadLength. 433 | // X bytes for the actual payload (calculated above). 434 | 435 | uint8_t frequency[totalLength] = { 0xEE, (uint8_t)(totalLength - 2), 436 | 0xEE, (uint8_t)(totalLength - 4), 437 | 0x40, 0x02, 0x00, 0x00, 438 | 0x68, frequencyPayloadLength }; 439 | 440 | size_t offset = FREQ_ALLHEADERSSIZE; 441 | 442 | // Each value <= 127 is 1 byte, above is 2 bytes. 443 | 444 | // Finger Frequency. 445 | frequency[offset++] = 0x80; // Finger Frequency identifier. 446 | frequency[offset++] = fingerFrequencyLength; 447 | frequency[offset++] = (uint8_t)fingerFrequencyValue[0]; 448 | if (fingerFrequencyLength == 2) 449 | { 450 | frequency[offset++] = (uint8_t)fingerFrequencyValue[1]; 451 | } 452 | 453 | // Idle Frequency. 454 | frequency[offset++] = 0x82; // Idle Frequency identifier. 455 | frequency[offset++] = idleFrequencyLength; 456 | frequency[offset++] = (uint8_t)idleFrequencyValue[0]; 457 | if (idleFrequencyLength == 2) 458 | { 459 | frequency[offset++] = (uint8_t)idleFrequencyValue[1]; 460 | } 461 | 462 | if (Write(frequency)) // We assume that the end user has called GetMessage prior to calling this method 463 | { 464 | failed = true; 465 | } 466 | else 467 | { 468 | lastSentMessage = MessageType::FREQUENCYTYPE; 469 | } 470 | 471 | return !failed; 472 | } 473 | 474 | 475 | bool Zforce::FlipXY(bool isFlipped) 476 | { 477 | bool failed = false; 478 | 479 | uint8_t flipXY[] = {0xEE, 0x0D, 0xEE, 0x0B, 0x40, 0x02, 0x02, 0x00, 0x73, 0x05, 0xA2, 0x03, 0x86, 0x01, (uint8_t)(isFlipped ? 0xFF : 0x00)}; 480 | 481 | if (Write(flipXY)) // We assume that the end user has called GetMessage prior to calling this method 482 | { 483 | failed = true; 484 | } 485 | else 486 | { 487 | lastSentMessage = MessageType::FLIPXYTYPE; 488 | } 489 | 490 | return !failed; 491 | } 492 | 493 | bool Zforce::ReverseX(bool isReversed) 494 | { 495 | bool failed = false; 496 | 497 | uint8_t reverseX[] = {0xEE, 0x0D, 0xEE, 0x0B, 0x40, 0x02, 0x02, 0x00, 0x73, 0x05, 0xA2, 0x03, 0x84, 0x01, (uint8_t)(isReversed ? 0xFF : 0x00)}; 498 | 499 | if (Write(reverseX)) // We assume that the end user has called GetMessage prior to calling this method 500 | { 501 | failed = true; 502 | } 503 | else 504 | { 505 | lastSentMessage = MessageType::REVERSEXTYPE; 506 | } 507 | 508 | return !failed; 509 | } 510 | 511 | bool Zforce::ReverseY(bool isReversed) 512 | { 513 | bool failed = false; 514 | 515 | uint8_t reverseY[] = {0xEE, 0x0D, 0xEE, 0x0B, 0x40, 0x02, 0x02, 0x00, 0x73, 0x05, 0xA2, 0x03, 0x85, 0x01, (uint8_t)(isReversed ? 0xFF : 0x00)}; 516 | 517 | if (Write(reverseY)) // We assume that the end user has called GetMessage prior to calling this method 518 | { 519 | failed = true; 520 | } 521 | else 522 | { 523 | lastSentMessage = MessageType::REVERSEYTYPE; 524 | } 525 | 526 | return !failed; 527 | } 528 | 529 | bool Zforce::ReportedTouches(uint8_t touches) 530 | { 531 | bool failed = false; 532 | 533 | if(touches > 10) 534 | { 535 | touches = 10; 536 | } 537 | 538 | uint8_t reportedTouches[] = {0xEE, 0x0B, 0xEE, 0x09, 0x40, 0x02, 0x02, 0x00, 0x73, 0x03, 0x86, 0x01, touches}; 539 | 540 | if (Write(reportedTouches)) // We assume that the end user has called GetMessage prior to calling this method 541 | { 542 | failed = true; 543 | } 544 | else 545 | { 546 | lastSentMessage = MessageType::REPORTEDTOUCHESTYPE; 547 | } 548 | 549 | return !failed; 550 | } 551 | 552 | bool Zforce::DetectionMode(bool mergeTouches, bool reflectiveEdgeFilter) 553 | { 554 | bool failed = false; 555 | 556 | uint8_t detectionModeValue = 0x00; 557 | detectionModeValue |= mergeTouches ? 0x20 : 0x00; // 0x20 as defined in the ASN.1 protocol 558 | detectionModeValue |= reflectiveEdgeFilter ? 0x80 : 0x00; // 0x80 as defined in the ASN.1 protocol 559 | uint8_t detectionMode[] = {0xEE, 0x0C, 0xEE, 0x0A, 0x40, 0x02, 0x02, 0x00, 0x73, 0x04, 0x85, 0x02, 0x00, detectionModeValue}; 560 | 561 | if (Write(detectionMode)) // We assume that the end user has called GetMessage prior to calling this method 562 | { 563 | failed = true; 564 | } 565 | else 566 | { 567 | lastSentMessage = MessageType::DETECTIONMODETYPE; 568 | } 569 | 570 | return !failed; 571 | } 572 | 573 | bool Zforce::TouchFormat() 574 | { 575 | bool failed = false; 576 | uint8_t touchFormat[] = {0xEE, 0x08, 0xEE, 0x06, 0x40, 0x02, 0x02, 0x00, 0x66, 0x00}; 577 | 578 | if (Write(touchFormat)) 579 | { 580 | failed = true; 581 | } 582 | else 583 | { 584 | lastSentMessage = MessageType::TOUCHFORMATTYPE; 585 | } 586 | 587 | return !failed; 588 | } 589 | 590 | bool Zforce::GetPlatformInformation() 591 | { 592 | bool failed = false; 593 | uint8_t platformInformation[] = {0xEE, 0x08, 0xEE, 0x06, 0x40, 0x02, 0x00, 0x00, 0x6C, 0x00}; 594 | 595 | if (Write(platformInformation)) 596 | { 597 | failed = true; 598 | } 599 | else 600 | { 601 | lastSentMessage = MessageType::PLATFORMINFORMATIONTYPE; 602 | } 603 | 604 | return !failed; 605 | } 606 | 607 | bool Zforce::TouchMode(uint8_t mode, int16_t clickOnTouchRadius, int16_t clickOnTouchTime) 608 | { 609 | bool failed = false; 610 | uint8_t serializedTime[2]; 611 | uint8_t serializedRadius[2]; 612 | 613 | uint8_t timeLength = SerializeInt(clickOnTouchTime, serializedTime); 614 | uint8_t radiusLength = SerializeInt(clickOnTouchRadius, serializedRadius); 615 | uint8_t combinedLength = timeLength + radiusLength; 616 | uint8_t touchMode[18 + combinedLength] = {0xEE, (uint8_t)(16 + combinedLength), 0xEE, (uint8_t)(14 + combinedLength), 0x40, 617 | 0x02, 0x02, 0x00, 0x7F, 0x24, (uint8_t)(7 + combinedLength), 618 | 0x80, 0x01, mode, 0x81, timeLength, serializedTime[0] }; 619 | 620 | uint8_t index = 17; 621 | if (timeLength == 2) 622 | { 623 | touchMode[index++] = serializedTime[1]; 624 | } 625 | 626 | touchMode[index++] = 0x82; 627 | touchMode[index++] = radiusLength; 628 | touchMode[index++] = serializedRadius[0]; 629 | 630 | if (radiusLength == 2) 631 | { 632 | touchMode[index] = serializedRadius[1]; 633 | } 634 | 635 | if (Write(touchMode)) 636 | { 637 | failed = true; 638 | } 639 | else 640 | { 641 | lastSentMessage = MessageType::TOUCHMODETYPE; 642 | } 643 | 644 | return !failed; 645 | } 646 | 647 | bool Zforce::FloatingProtection(bool enabled, uint16_t time) 648 | { 649 | bool failed = false; 650 | uint8_t serializedTime[2]; 651 | uint8_t timeLength = SerializeInt(time, serializedTime); 652 | uint8_t floatingProtection[17 + timeLength] = {0xEE, (uint8_t)(15 + timeLength), 0xEE, (uint8_t)(13 + timeLength), 0x40, 0x02, 0x02, 0x00, 0x73, 653 | (uint8_t)(7 + timeLength), 0xA8, (uint8_t)(5 + timeLength), 0x80, 0x01, (uint8_t)(enabled ? 0xFF : 0x00), 0x81, 654 | timeLength, serializedTime[0]}; 655 | 656 | if (timeLength == 2) 657 | { 658 | floatingProtection[18] = serializedTime[1]; 659 | } 660 | 661 | if (Write(floatingProtection)) 662 | { 663 | failed = true; 664 | } 665 | else 666 | { 667 | lastSentMessage = MessageType::FLOATINGPROTECTIONTYPE; 668 | } 669 | 670 | return !failed; 671 | } 672 | 673 | int Zforce::GetDataReady() 674 | { 675 | return digitalRead(dataReady); 676 | } 677 | 678 | Message* Zforce::GetMessage() 679 | { 680 | Message* msg = nullptr; 681 | if(GetDataReady() == HIGH) 682 | { 683 | if(!Read(buffer)) 684 | { 685 | msg = VirtualParse(buffer); 686 | ClearBuffer(buffer); 687 | } 688 | } 689 | 690 | return msg; 691 | } 692 | 693 | void Zforce::DestroyMessage(Message* msg) 694 | { 695 | delete msg; 696 | msg = nullptr; 697 | } 698 | 699 | Message* Zforce::VirtualParse(uint8_t* payload) 700 | { 701 | Message* msg = nullptr; 702 | 703 | switch(payload[2]) // Check if the payload is a response to a request or if it's a notification. 704 | { 705 | case 0xEF: 706 | { 707 | ParseResponse(payload, &msg); 708 | } 709 | break; 710 | case 0xF0: 711 | { 712 | if (payload[8] == 0xA0) // Check the identifier if this is a touch message or something else. 713 | { 714 | if (this->touchDescriptorInitialized) 715 | { 716 | msg = new TouchMessage; 717 | msg->type = MessageType::TOUCHTYPE; 718 | ParseTouch((TouchMessage*)msg, payload); 719 | } 720 | } 721 | else if (payload[8] == 0x63) 722 | { 723 | msg = new Message; 724 | msg->type = MessageType::BOOTCOMPLETETYPE; 725 | } 726 | } 727 | break; 728 | 729 | default: 730 | break; 731 | } 732 | 733 | lastSentMessage = MessageType::NONE; 734 | return msg; 735 | } 736 | 737 | void Zforce::ParseResponse(uint8_t* payload, Message** msg) 738 | { 739 | switch(lastSentMessage) 740 | { 741 | case MessageType::REVERSEYTYPE: 742 | { 743 | (*(msg)) = new ReverseYMessage; 744 | (*(msg))->type = MessageType::REVERSEYTYPE; 745 | ParseReverseY((ReverseYMessage*)(*(msg)), payload); 746 | } 747 | break; 748 | case MessageType::ENABLETYPE: 749 | { 750 | (*(msg)) = new EnableMessage; 751 | (*(msg))->type = MessageType::ENABLETYPE; 752 | ParseEnable((EnableMessage*)(*(msg)), payload); 753 | } 754 | break; 755 | case MessageType::TOUCHACTIVEAREATYPE: 756 | { 757 | (*(msg)) = new TouchActiveAreaMessage; 758 | (*(msg))->type = MessageType::TOUCHACTIVEAREATYPE; 759 | ParseTouchActiveArea((TouchActiveAreaMessage*)(*(msg)), payload); 760 | } 761 | break; 762 | case MessageType::REVERSEXTYPE: 763 | { 764 | (*(msg)) = new ReverseXMessage; 765 | (*(msg))->type = MessageType::REVERSEXTYPE; 766 | ParseReverseX((ReverseXMessage*)(*(msg)), payload); 767 | } 768 | break; 769 | case MessageType::FLIPXYTYPE: 770 | { 771 | (*(msg)) = new FlipXYMessage; 772 | (*(msg))->type = MessageType::FLIPXYTYPE; 773 | ParseFlipXY((FlipXYMessage*)(*(msg)), payload); 774 | } 775 | break; 776 | case MessageType::REPORTEDTOUCHESTYPE: 777 | { 778 | (*(msg)) = new ReportedTouchesMessage; 779 | (*(msg))->type = MessageType::REPORTEDTOUCHESTYPE; 780 | ParseReportedTouches((ReportedTouchesMessage*)(*(msg)), payload); 781 | } 782 | break; 783 | case MessageType::FREQUENCYTYPE: 784 | { 785 | (*(msg)) = new FrequencyMessage; 786 | (*(msg))->type = MessageType::FREQUENCYTYPE; 787 | ParseFrequency((FrequencyMessage*)(*(msg)), payload); 788 | } 789 | break; 790 | case MessageType::DETECTIONMODETYPE: 791 | { 792 | (*(msg)) = new DetectionModeMessage; 793 | (*(msg))->type = MessageType::DETECTIONMODETYPE; 794 | ParseDetectionMode((DetectionModeMessage*)(*(msg)), payload); 795 | } 796 | break; 797 | case MessageType::TOUCHFORMATTYPE: 798 | { 799 | (*(msg)) = new TouchDescriptorMessage; 800 | (*(msg))->type = MessageType::TOUCHFORMATTYPE; 801 | ParseTouchDescriptor((TouchDescriptorMessage*)(*(msg)), payload); 802 | } 803 | break; 804 | case MessageType::TOUCHMODETYPE: 805 | { 806 | (*(msg)) = new TouchModeMessage; 807 | (*(msg))->type = MessageType::TOUCHMODETYPE; 808 | ParseTouchMode((TouchModeMessage*)(*(msg)), payload); 809 | } 810 | break; 811 | case MessageType::FLOATINGPROTECTIONTYPE: 812 | { 813 | (*(msg)) = new FloatingProtectionMessage; 814 | (*(msg))->type = MessageType::FLOATINGPROTECTIONTYPE; 815 | ParseFloatingProtection((FloatingProtectionMessage *)(*(msg)), payload); 816 | } 817 | break; 818 | case MessageType::PLATFORMINFORMATIONTYPE: 819 | { 820 | (*(msg)) = new PlatformInformationMessage; 821 | (*(msg))->type = MessageType::PLATFORMINFORMATIONTYPE; 822 | ParsePlatformInformation((PlatformInformationMessage*)(*(msg)), &payload[2], payload[1] - 1); 823 | } 824 | break; 825 | default: 826 | { 827 | (*(msg)) = new Message; 828 | (*(msg))->type = MessageType::NONE; 829 | } 830 | break; 831 | } 832 | } 833 | 834 | void Zforce::ParseTouchDescriptor(TouchDescriptorMessage* msg, uint8_t* payload) 835 | { 836 | uint8_t amountBits = ((payload[11] - 1) * 8) - payload[12]; 837 | 838 | uint32_t descr = 0; 839 | descr |= (uint32_t)payload[13] << 24; 840 | descr |= (uint32_t)payload[14] << 16; 841 | descr |= (uint32_t)payload[15] << 8; 842 | 843 | msg->descriptor = new TouchDescriptor[(int)TouchDescriptor::MaxValue]; 844 | uint8_t bitIndex = 0; 845 | uint8_t descIndex = 0; 846 | while (bitIndex <= amountBits) 847 | { 848 | if (descr & (0x80000000 >> bitIndex)) 849 | { 850 | msg->descriptor[descIndex++] = (TouchDescriptor)bitIndex; 851 | } 852 | bitIndex++; 853 | } 854 | 855 | touchMetaInformation.touchDescriptor = new TouchDescriptor[descIndex]; 856 | touchMetaInformation.touchByteCount = descIndex; 857 | for (int i = 0; i < touchMetaInformation.touchByteCount; i++) 858 | { 859 | touchMetaInformation.touchDescriptor[i] = msg->descriptor[i]; 860 | } 861 | } 862 | 863 | void Zforce::ParsePlatformInformation(PlatformInformationMessage *msg, uint8_t *rawData, uint32_t length) 864 | { 865 | (void)length; 866 | uint16_t value = 0; 867 | uint16_t valueLength = 0; 868 | 869 | rawData += GetNumLengthBytes(&rawData[1]) + 5; // Skip response byte + ASN.1 length + address 870 | rawData += GetNumLengthBytes(&rawData[1]) + 1; // Skip ASN.1 Device Information + length 871 | 872 | uint32_t platformInformationLength = GetLength(&rawData[1]); 873 | rawData += GetNumLengthBytes(&rawData[1]) + 1; // PlatformInformation length + PlatformInformation application identifier 874 | 875 | uint32_t position = 0; 876 | 877 | while (position < platformInformationLength) 878 | { 879 | switch (rawData[position++]) 880 | { 881 | case 0x84: // FirmwareVersionMajor 882 | { 883 | valueLength = rawData[position++]; 884 | if (valueLength == 2) 885 | { 886 | value = rawData[position++] << 8; 887 | value |= rawData[position++]; 888 | } 889 | else 890 | { 891 | value = rawData[position++]; 892 | } 893 | 894 | msg->firmwareVersionMajor = value; 895 | break; 896 | case 0x85: // FirmwareVersionMinor 897 | valueLength = rawData[position++]; 898 | if (valueLength == 2) 899 | { 900 | value = rawData[position++] << 8; 901 | value |= rawData[position++]; 902 | } 903 | else 904 | { 905 | value = rawData[position++]; 906 | } 907 | 908 | msg->firmwareVersionMinor = value; 909 | break; 910 | } 911 | case 0x8A: // MCUUniqueIdentifier 912 | { 913 | uint8_t *MCUUniqueIdentifier = nullptr; 914 | uint32_t MCUUniqueIdentifierLength; 915 | DecodeOctetString(rawData, &position, &MCUUniqueIdentifierLength, &MCUUniqueIdentifier); 916 | 917 | // Each byte gets converted into its hex representation, which takes 2 bytes, then we add space for the null byte. 918 | const uint32_t bufferLength = (MCUUniqueIdentifierLength * 2) + 1; 919 | 920 | char* mcuIdBuffer = (char *)malloc(bufferLength); 921 | mcuIdBuffer[MCUUniqueIdentifierLength - 1] = 0; // Add null byte. 922 | int writeSize = 0; 923 | for (size_t i = 0; i < MCUUniqueIdentifierLength; i++) 924 | { 925 | writeSize += snprintf(mcuIdBuffer + writeSize, bufferLength - writeSize, "%02X", MCUUniqueIdentifier[i]); 926 | } 927 | free(MCUUniqueIdentifier); 928 | msg->mcuUniqueIdentifier = mcuIdBuffer; 929 | msg->mcuUniqueIdentifierLength = writeSize; 930 | break; 931 | } 932 | default: 933 | { 934 | position += rawData[position]; 935 | position++; 936 | break; 937 | } 938 | } 939 | } 940 | } 941 | 942 | void Zforce::ParseTouchMode(TouchModeMessage* msg, uint8_t* payload) 943 | { 944 | const uint8_t offset = 9; 945 | uint16_t valueLength = 0; 946 | 947 | msg->clickOnTouchTime = -1; // FW <=1.55 doesn't reply properly when mode = Disabled. 948 | msg->clickOnTouchRadius = -1; // We set them to -1, signaling the data is invalid. 949 | 950 | for (int i = offset; i < payload[10] + offset; i++) 951 | { 952 | switch (payload[i]) 953 | { 954 | case 0x80: // TouchMode 955 | msg->mode = (TouchModes)payload[i + 2]; 956 | break; 957 | case 0x81: // ClickOnTouchTime 958 | valueLength = payload[i + 1]; 959 | if (valueLength == 2) 960 | { 961 | msg->clickOnTouchTime = payload[i + 2] << 8; 962 | msg->clickOnTouchTime |= payload[i + 3]; 963 | } 964 | else 965 | { 966 | msg->clickOnTouchTime = payload[i + 2]; 967 | } 968 | break; 969 | case 0x82: // ClickOnTouchRadius 970 | valueLength = payload[i + 1]; 971 | if (valueLength == 2) 972 | { 973 | msg->clickOnTouchRadius = payload[i + 2] << 8; 974 | msg->clickOnTouchRadius |= payload[i + 3]; 975 | } 976 | else 977 | { 978 | msg->clickOnTouchRadius = payload[i + 2]; 979 | } 980 | break; 981 | default: 982 | break; 983 | } 984 | } 985 | } 986 | 987 | void Zforce::ParseFrequency(FrequencyMessage* msg, uint8_t* payload) 988 | { 989 | const uint8_t offset = 8; 990 | uint16_t value = 0; 991 | uint16_t valueLength = 0; 992 | 993 | for (int i = offset; i < payload[9] + offset; i++) 994 | { 995 | switch (payload[i]) 996 | { 997 | case 0x80: // Finger Frequency 998 | valueLength = payload[i + 1]; 999 | 1000 | if (valueLength == 2) 1001 | { 1002 | value = payload[i + 2] << 8; 1003 | value |= payload[i + 3]; 1004 | } 1005 | else 1006 | { 1007 | value = payload[i + 2]; 1008 | } 1009 | msg->fingerFrequency = value; 1010 | break; 1011 | 1012 | case 0x82: //Idle Frequency 1013 | valueLength = payload[i + 1]; 1014 | 1015 | if (valueLength == 2) 1016 | { 1017 | value = payload[i + 2] << 8; 1018 | value |= payload[i + 3]; 1019 | } 1020 | else 1021 | { 1022 | value = payload[i + 2]; 1023 | } 1024 | msg->idleFrequency = value; 1025 | break; 1026 | 1027 | default: 1028 | break; 1029 | } 1030 | } 1031 | } 1032 | 1033 | void Zforce::ParseTouchActiveArea(TouchActiveAreaMessage* msg, uint8_t* payload) 1034 | { 1035 | const uint8_t offset = 10; 1036 | uint16_t value = 0; 1037 | uint16_t valueLength = 0; 1038 | 1039 | for (int i = offset; i < payload[11] + offset; i++) // 10 = index for TouchActiveArea struct, 11 index for length of TouchActiveArea struct 1040 | { 1041 | switch (payload[i]) 1042 | { 1043 | case 0x80: // MinX 1044 | valueLength = payload[i + 1]; 1045 | if (valueLength == 2) 1046 | { 1047 | value = payload[i + 2] << 8; 1048 | value |= payload[i + 3]; 1049 | } 1050 | else 1051 | { 1052 | value = payload[i + 2]; 1053 | } 1054 | msg->minX = value; 1055 | break; 1056 | 1057 | case 0x81: // MinY 1058 | valueLength = payload[i + 1]; 1059 | if (valueLength == 2) 1060 | { 1061 | value = payload[i + 2] << 8; 1062 | value |= payload[i + 3]; 1063 | } 1064 | else 1065 | { 1066 | value = payload[i + 2]; 1067 | } 1068 | msg->minY = value; 1069 | break; 1070 | 1071 | case 0x82: // MaxX 1072 | valueLength = payload[i + 1]; 1073 | if (valueLength == 2) 1074 | { 1075 | value = payload[i + 2] << 8; 1076 | value |= payload[i + 3]; 1077 | } 1078 | else 1079 | { 1080 | value = payload[i + 2]; 1081 | } 1082 | msg->maxX = value; 1083 | break; 1084 | 1085 | case 0x83: // MaxY 1086 | valueLength = payload[i + 1]; 1087 | if (valueLength == 2) 1088 | { 1089 | value = payload[i + 2] << 8; 1090 | value |= payload[i + 3]; 1091 | } 1092 | else 1093 | { 1094 | value = payload[i + 2]; 1095 | } 1096 | msg->maxY = value; 1097 | break; 1098 | 1099 | default: 1100 | break; 1101 | } 1102 | } 1103 | } 1104 | 1105 | void Zforce::ParseEnable(EnableMessage* msg, uint8_t* payload) 1106 | { 1107 | switch (payload[10]) 1108 | { 1109 | case 0x80: 1110 | msg->enabled = false; 1111 | break; 1112 | 1113 | case 0x81: 1114 | msg->enabled = true; 1115 | break; 1116 | 1117 | default: 1118 | break; 1119 | } 1120 | } 1121 | 1122 | void Zforce::ParseReportedTouches(ReportedTouchesMessage* msg, uint8_t* payload) 1123 | { 1124 | const uint8_t offset = 10; 1125 | for(int i = offset + payload[11]; i < payload[9] + offset; i++) 1126 | { 1127 | if(payload[i] == 0x86) 1128 | { 1129 | msg->reportedTouches = payload[i + 2]; 1130 | break; 1131 | } 1132 | } 1133 | } 1134 | 1135 | void Zforce::ParseFloatingProtection(FloatingProtectionMessage* msg, uint8_t* payload) 1136 | { 1137 | const uint8_t offset = 10; 1138 | uint8_t valueLength = 0; 1139 | msg->time = 0; 1140 | 1141 | for(int i = offset + payload[11]; i < payload[9] + offset; i++) 1142 | { 1143 | if (payload[i] == 0xA8) // Identifier floating protection 1144 | { 1145 | for (int j = 0; j < payload[i + 1]; j++) 1146 | { 1147 | if (payload[i + j] == 0x80) // Identifier floating protection enabled 1148 | { 1149 | msg->enabled = (bool)payload[i + j + 2]; 1150 | } 1151 | if (payload[i + j] == 0x81) // Identifier for time 1152 | { 1153 | valueLength = payload[i + j + 1]; 1154 | if (valueLength == 2) 1155 | { 1156 | msg->time = payload[i + j + 2] << 8; 1157 | msg->time |= payload[i + j + 3]; 1158 | } 1159 | else 1160 | { 1161 | msg->time = payload[i + j + 2]; 1162 | } 1163 | break; 1164 | } 1165 | } 1166 | } 1167 | } 1168 | } 1169 | 1170 | void Zforce::ParseReverseX(ReverseXMessage* msg, uint8_t* payload) 1171 | { 1172 | const uint8_t offset = 10; 1173 | for(int i = offset; i < payload[11] + offset; i++) 1174 | { 1175 | if(payload[i] == 0x84) 1176 | { 1177 | msg->reversed = (bool)payload[i + 2]; 1178 | break; 1179 | } 1180 | } 1181 | } 1182 | 1183 | void Zforce::ParseReverseY(ReverseYMessage* msg, uint8_t* payload) 1184 | { 1185 | const uint8_t offset = 10; 1186 | for(int i = offset; i < payload[11] + offset; i++) 1187 | { 1188 | if(payload[i] == 0x85) 1189 | { 1190 | msg->reversed = (bool)payload[i + 2]; 1191 | break; 1192 | } 1193 | } 1194 | } 1195 | 1196 | void Zforce::ParseFlipXY(FlipXYMessage* msg, uint8_t* payload) 1197 | { 1198 | const uint8_t offset = 10; 1199 | for(int i = offset; i < payload[11] + offset; i++) 1200 | { 1201 | if(payload[i] == 0x86) 1202 | { 1203 | msg->flipXY = (bool)payload[i + 2]; 1204 | break; 1205 | } 1206 | } 1207 | } 1208 | 1209 | void Zforce::ParseDetectionMode(DetectionModeMessage* msg, uint8_t* payload) 1210 | { 1211 | uint8_t offset = payload[11] + 11; // 11 = Index for TouchActiveArea length 1212 | offset += payload[offset + 2]; // Add the length of the following Sequence to the offset 1213 | const uint8_t length = payload[1] + 2; 1214 | 1215 | for (int i = offset; i < length; i++) 1216 | { 1217 | if (payload[i] == 0x85) // Primitive type = 0x60, context specific = 0x20, tag id = 0x05 => 0x85 1218 | { 1219 | // We found it 1220 | uint8_t valueLength = payload[i + 1]; 1221 | msg->mergeTouches = (payload[i + valueLength + 1] & 0x20) != 0; 1222 | msg->reflectiveEdgeFilter = (payload[i + valueLength + 1] & 0x80) != 0; 1223 | break; 1224 | } 1225 | else 1226 | { 1227 | // Keep looking 1228 | i += payload[i + 1] + 1; 1229 | } 1230 | } 1231 | } 1232 | 1233 | void Zforce::ParseTouch(TouchMessage* msg, uint8_t* payload) 1234 | { 1235 | 1236 | if (touchMetaInformation.touchDescriptor == nullptr) 1237 | { 1238 | return; 1239 | } 1240 | else 1241 | { 1242 | const uint8_t payloadOffset = 12; 1243 | const uint8_t expectedTouchLength = touchMetaInformation.touchByteCount + 2; 1244 | msg->touchCount = payload[9] / expectedTouchLength; 1245 | msg->touchData = new TouchData[msg->touchCount]; 1246 | msg->timestamp = 0; 1247 | 1248 | if ((payload[1] + 2) > (payloadOffset + (expectedTouchLength * msg->touchCount))) // Check for timestamp 1249 | { 1250 | uint8_t timestampIndex = payloadOffset + (expectedTouchLength * msg->touchCount) - 2; 1251 | if (payload[timestampIndex] == 0x58) // Check for timestamp identifier 1252 | { 1253 | uint8_t timestampLength = payload[timestampIndex + 1]; 1254 | for (int index = (timestampIndex + 2); index < (timestampIndex + 2 + timestampLength); index++) 1255 | { 1256 | msg->timestamp <<= 8; 1257 | msg->timestamp |= payload[index]; 1258 | } 1259 | } 1260 | } 1261 | 1262 | for (uint8_t i = 0; i < msg->touchCount; i++) 1263 | { 1264 | for (uint8_t j = 0; j < touchMetaInformation.touchByteCount; j++) 1265 | { 1266 | uint8_t index = payloadOffset + j + (i * expectedTouchLength); 1267 | switch (touchMetaInformation.touchDescriptor[j]) 1268 | { 1269 | case TouchDescriptor::Id: 1270 | { 1271 | msg->touchData[i].id = payload[index]; 1272 | break; 1273 | } 1274 | case TouchDescriptor::Event: 1275 | { 1276 | msg->touchData[i].event = (TouchEvent)payload[index]; 1277 | break; 1278 | } 1279 | case TouchDescriptor::LocXByte1: 1280 | { 1281 | msg->touchData[i].x = payload[index]; 1282 | break; 1283 | } 1284 | case TouchDescriptor::LocXByte2: 1285 | { 1286 | msg->touchData[i].x <<= 8; 1287 | msg->touchData[i].x |= payload[index]; 1288 | break; 1289 | } 1290 | case TouchDescriptor::LocXByte3: 1291 | { 1292 | msg->touchData[i].x <<= 8; 1293 | msg->touchData[i].x |= payload[index]; 1294 | break; 1295 | } 1296 | case TouchDescriptor::LocYByte1: 1297 | { 1298 | msg->touchData[i].y = payload[index]; 1299 | break; 1300 | } 1301 | case TouchDescriptor::LocYByte2: 1302 | { 1303 | msg->touchData[i].y <<= 8; 1304 | msg->touchData[i].y |= payload[index]; 1305 | break; 1306 | } 1307 | case TouchDescriptor::LocYByte3: 1308 | { 1309 | msg->touchData[i].y <<= 8; 1310 | msg->touchData[i].y |= payload[index]; 1311 | break; 1312 | } 1313 | case TouchDescriptor::LocZByte1: 1314 | { 1315 | // No support for Z in Neonode AIR Touch sensor 1316 | break; 1317 | } 1318 | case TouchDescriptor::LocZByte2: 1319 | { 1320 | // No support for Z in Neonode AIR Touch sensor 1321 | break; 1322 | } 1323 | case TouchDescriptor::LocZByte3: 1324 | { 1325 | // No support for Z in Neonode AIR Touch sensor 1326 | break; 1327 | } 1328 | case TouchDescriptor::SizeXByte1: 1329 | { 1330 | msg->touchData[i].sizeX = payload[index]; 1331 | break; 1332 | } 1333 | case TouchDescriptor::SizeXByte2: 1334 | { 1335 | msg->touchData[i].sizeX <<= 8; 1336 | msg->touchData[i].sizeX |= payload[index]; 1337 | break; 1338 | } 1339 | case TouchDescriptor::SizeXByte3: 1340 | { 1341 | msg->touchData[i].sizeX <<= 8; 1342 | msg->touchData[i].sizeX |= payload[index]; 1343 | break; 1344 | } 1345 | case TouchDescriptor::SizeYByte1: 1346 | { 1347 | // No support for size Y in Neonode AIR Touch sensor 1348 | break; 1349 | } 1350 | case TouchDescriptor::SizeYByte2: 1351 | { 1352 | // No support for size Y in Neonode AIR Touch sensor 1353 | break; 1354 | } 1355 | case TouchDescriptor::SizeYByte3: 1356 | { 1357 | // No support for size Y in Neonode AIR Touch sensor 1358 | break; 1359 | } 1360 | case TouchDescriptor::SizeZByte1: 1361 | { 1362 | // No support for size Z in Neonode AIR Touch sensor 1363 | break; 1364 | } 1365 | case TouchDescriptor::SizeZByte2: 1366 | { 1367 | // No support for size Z in Neonode AIR Touch sensor 1368 | break; 1369 | } 1370 | case TouchDescriptor::SizeZByte3: 1371 | { 1372 | // No support for size Z in Neonode AIR Touch sensor 1373 | break; 1374 | } 1375 | case TouchDescriptor::Orientation: 1376 | { 1377 | // No support for Orientation in Neonode AIR Touch sensor 1378 | break; 1379 | } 1380 | case TouchDescriptor::Confidence: 1381 | { 1382 | // Confidence is reported but should not be used as it is always reported as 100% 1383 | break; 1384 | } 1385 | case TouchDescriptor::Pressure: 1386 | { 1387 | // There iws no support for pressure in Neonode AIR Touch sensor 1388 | break; 1389 | } 1390 | default: 1391 | break; 1392 | } 1393 | } 1394 | } 1395 | } 1396 | } 1397 | 1398 | void Zforce::ClearBuffer(uint8_t* buffer) 1399 | { 1400 | memset(buffer, 0, BUFFER_SIZE); 1401 | } 1402 | 1403 | uint8_t Zforce::SerializeInt(int32_t value, uint8_t* serialized) 1404 | { 1405 | if (value < 128) 1406 | { 1407 | serialized[0] = (uint8_t)(value & 0xFF); 1408 | return 1; 1409 | } 1410 | else 1411 | { 1412 | serialized[0] = (uint8_t)(value >> 8); 1413 | serialized[1] = (uint8_t)(value & 0xFF); 1414 | return 2; 1415 | } 1416 | } 1417 | 1418 | void Zforce::DecodeOctetString(uint8_t* rawData, uint32_t* position, uint32_t* destinationLength, uint8_t** destination) 1419 | { 1420 | uint32_t length = rawData[(*position)++]; 1421 | *destinationLength = length; 1422 | *destination = (uint8_t*)malloc(length); 1423 | memcpy(*destination, &rawData[(*position)], length); 1424 | (*position) += length; 1425 | } 1426 | 1427 | uint16_t Zforce::GetLength(uint8_t* rawData) 1428 | { 1429 | int numLengthBytes = 0; 1430 | int length = 0; 1431 | if (rawData[0] & 0x80) // We have long length form 1432 | { 1433 | numLengthBytes = rawData[0] - 0x80; 1434 | for (int i = 0; i < numLengthBytes; i++) 1435 | { 1436 | length += rawData[i + 1]; 1437 | } 1438 | } 1439 | else 1440 | { 1441 | length = rawData[0]; 1442 | } 1443 | 1444 | return length; 1445 | } 1446 | 1447 | uint8_t Zforce::GetNumLengthBytes(uint8_t* rawData) 1448 | { 1449 | uint8_t numLengthBytes = 1; 1450 | if (rawData[0] & 0x80) // We have long length form 1451 | { 1452 | numLengthBytes = (rawData[0] - 0x80) + 1; 1453 | } 1454 | 1455 | return numLengthBytes; 1456 | } 1457 | 1458 | Zforce zforce = Zforce(); 1459 | -------------------------------------------------------------------------------- /src/Zforce.h: -------------------------------------------------------------------------------- 1 | /* Neonode zForce v7 interface library for Arduino 2 | 3 | Copyright (C) 2019-2023 Neonode Inc. 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | #pragma once 20 | 21 | // Largest transaction size, excluding i2c header. 22 | #define MAX_PAYLOAD 255 23 | // The buffer must be able to contain both the i2c header, and MAX_PAYLOAD size. 24 | #define BUFFER_SIZE (MAX_PAYLOAD+2) 25 | #define ZFORCE_DEFAULT_I2C_ADDRESS 0x50 26 | 27 | enum TouchEvent 28 | { 29 | DOWN = 0, 30 | MOVE = 1, 31 | UP = 2, 32 | INVALID = 3, 33 | GHOST = 4 34 | }; 35 | 36 | enum class MessageType 37 | { 38 | NONE = 0, 39 | ENABLETYPE = 1, 40 | TOUCHACTIVEAREATYPE = 2, 41 | REVERSEXTYPE = 3, 42 | REVERSEYTYPE = 4, 43 | FLIPXYTYPE = 5, 44 | REPORTEDTOUCHESTYPE = 6, 45 | TOUCHTYPE = 7, 46 | BOOTCOMPLETETYPE = 8, 47 | FREQUENCYTYPE = 9, 48 | DETECTIONMODETYPE = 10, 49 | TOUCHFORMATTYPE = 11, 50 | TOUCHMODETYPE = 12, 51 | FLOATINGPROTECTIONTYPE = 13, 52 | PLATFORMINFORMATIONTYPE = 14 53 | }; 54 | 55 | typedef struct TouchData 56 | { 57 | uint32_t x; 58 | uint32_t y; 59 | uint32_t sizeX; //the estimated diameter of the touch object 60 | uint8_t id; 61 | TouchEvent event; 62 | } TouchData; 63 | 64 | enum class TouchModes 65 | { 66 | NORMAL, 67 | CLICKONTOUCH, 68 | UNSUPPORTED 69 | }; 70 | 71 | typedef struct Message 72 | { 73 | virtual ~Message() 74 | { 75 | 76 | } 77 | MessageType type; 78 | } Message; 79 | 80 | typedef struct TouchMessage : public Message 81 | { 82 | virtual ~TouchMessage() 83 | { 84 | delete[] touchData; 85 | touchData = nullptr; 86 | } 87 | uint32_t timestamp; 88 | uint8_t touchCount; 89 | TouchData* touchData; 90 | } TouchMessage; 91 | 92 | typedef struct EnableMessage : public Message 93 | { 94 | virtual ~EnableMessage() 95 | { 96 | 97 | } 98 | bool enabled; 99 | } EnableMessage; 100 | 101 | typedef struct TouchActiveAreaMessage : public Message 102 | { 103 | virtual ~TouchActiveAreaMessage() 104 | { 105 | 106 | } 107 | uint16_t minX; 108 | uint16_t minY; 109 | uint16_t maxX; 110 | uint16_t maxY; 111 | } TouchActiveAreaMessage; 112 | 113 | typedef struct FrequencyMessage : public Message 114 | { 115 | virtual ~FrequencyMessage() 116 | { 117 | 118 | } 119 | uint16_t idleFrequency; 120 | uint16_t fingerFrequency; 121 | } FrequencyMessage; 122 | 123 | 124 | typedef struct FlipXYMessage : public Message 125 | { 126 | virtual ~FlipXYMessage() 127 | { 128 | 129 | } 130 | bool flipXY; 131 | } FlipXYMessage; 132 | 133 | typedef struct ReverseXMessage : public Message 134 | { 135 | virtual ~ReverseXMessage() 136 | { 137 | 138 | } 139 | bool reversed; 140 | } ReverseXMessage; 141 | 142 | typedef struct ReverseYMessage : public Message 143 | { 144 | virtual ~ReverseYMessage() 145 | { 146 | 147 | } 148 | bool reversed; 149 | } ReverseYMessage; 150 | 151 | typedef struct ReportedTouchesMessage : public Message 152 | { 153 | virtual ~ReportedTouchesMessage() 154 | { 155 | 156 | } 157 | uint8_t reportedTouches; 158 | } ReportedTouchesMessage; 159 | 160 | typedef struct DetectionModeMessage : public Message 161 | { 162 | virtual ~DetectionModeMessage() 163 | { 164 | 165 | } 166 | bool mergeTouches; 167 | bool reflectiveEdgeFilter; 168 | } DetectionModeMessage; 169 | 170 | typedef struct TouchModeMessage : public Message 171 | { 172 | virtual ~TouchModeMessage() 173 | { 174 | 175 | } 176 | TouchModes mode; 177 | int clickOnTouchRadius; 178 | int clickOnTouchTime; 179 | } TouchModeMessage; 180 | 181 | enum class TouchDescriptor : uint8_t 182 | { 183 | Id = 0, 184 | Event = 1, 185 | LocXByte1 = 2, 186 | LocXByte2 = 3, 187 | LocXByte3 = 4, 188 | LocYByte1 = 5, 189 | LocYByte2 = 6, 190 | LocYByte3 = 7, 191 | LocZByte1 = 8 , 192 | LocZByte2 = 9, 193 | LocZByte3 = 10, 194 | SizeXByte1 = 11, 195 | SizeXByte2 = 12, 196 | SizeXByte3 = 13, 197 | SizeYByte1 = 14, 198 | SizeYByte2 = 15, 199 | SizeYByte3 = 16, 200 | SizeZByte1 = 17, 201 | SizeZByte2 = 18, 202 | SizeZByte3 = 19, 203 | Orientation = 20, 204 | Confidence = 21, 205 | Pressure = 22, 206 | MaxValue = 23 // Maximum value of enum 207 | }; 208 | 209 | typedef struct TouchDescriptorMessage : public Message 210 | { 211 | virtual ~TouchDescriptorMessage() 212 | { 213 | delete descriptor; 214 | descriptor = nullptr; 215 | } 216 | TouchDescriptor *descriptor; 217 | 218 | } TouchDescriptorMessage; 219 | 220 | typedef struct PlatformInformationMessage : public Message 221 | { 222 | virtual ~PlatformInformationMessage() 223 | { 224 | delete[] mcuUniqueIdentifier; 225 | mcuUniqueIdentifier = nullptr; 226 | } 227 | uint8_t firmwareVersionMajor; 228 | uint8_t firmwareVersionMinor; 229 | char* mcuUniqueIdentifier; 230 | uint8_t mcuUniqueIdentifierLength; 231 | } PlatformInformationMessage; 232 | 233 | typedef struct FloatingProtectionMessage : public Message 234 | { 235 | virtual ~FloatingProtectionMessage() 236 | { 237 | 238 | } 239 | bool enabled; 240 | uint16_t time; 241 | } FloatingProtectionMessage; 242 | 243 | typedef struct TouchMetaInformation 244 | { 245 | TouchDescriptor *touchDescriptor; 246 | uint8_t touchByteCount = 0; 247 | } TouchMetaInformation; 248 | 249 | class Zforce 250 | { 251 | public: 252 | Zforce(); 253 | void Start(int dr); 254 | void Start(int dr, int i2cAddress); 255 | int Read(uint8_t* payload); 256 | int Write(uint8_t* payload); 257 | bool SendRawMessage(uint8_t* payload, uint8_t payloadLength); 258 | uint8_t* ReceiveRawMessage(uint8_t* receivedLength, uint16_t *remainingLength); 259 | bool Enable(bool isEnabled); 260 | bool GetEnable(); 261 | bool TouchActiveArea(uint16_t minX, uint16_t minY, uint16_t maxX, uint16_t maxY); 262 | bool FlipXY(bool isFlipped); 263 | bool ReverseX(bool isReversed); 264 | bool ReverseY(bool isReversed); 265 | bool Frequency(uint16_t idleFrequency, uint16_t fingerFrequency); 266 | bool ReportedTouches(uint8_t touches); 267 | bool DetectionMode(bool mergeTouches, bool reflectiveEdgeFilter); 268 | bool TouchFormat(); 269 | bool TouchMode(uint8_t mode, int16_t clickOnTouchRadius, int16_t clickOnTouchTime); 270 | bool FloatingProtection(bool enabled, uint16_t time); 271 | int GetDataReady(); 272 | Message* GetMessage(); 273 | void DestroyMessage(Message * msg); 274 | bool GetPlatformInformation(); 275 | uint8_t FirmwareVersionMajor; 276 | uint8_t FirmwareVersionMinor; 277 | char* MCUUniqueIdentifier; 278 | private: 279 | Message* VirtualParse(uint8_t* payload); 280 | void ParseTouchActiveArea(TouchActiveAreaMessage* msg, uint8_t* payload); 281 | void ParseEnable(EnableMessage* msg, uint8_t* payload); 282 | void ParseFrequency(FrequencyMessage* msg, uint8_t* payload); 283 | void ParseReportedTouches(ReportedTouchesMessage* msg, uint8_t* payload); 284 | void ParseReverseX(ReverseXMessage* msg, uint8_t* payload); 285 | void ParseReverseY(ReverseYMessage* msg, uint8_t* payload); 286 | void ParseFlipXY(FlipXYMessage* msg, uint8_t* payload); 287 | void ParseTouch(TouchMessage* msg, uint8_t* payload); 288 | void ParseDetectionMode(DetectionModeMessage* msg, uint8_t* payload); 289 | void ParseResponse(uint8_t* payload, Message** msg); 290 | void ParseTouchDescriptor(TouchDescriptorMessage* msg, uint8_t* payload); 291 | void ParseTouchMode(TouchModeMessage* msg, uint8_t* payload); 292 | void ParseFloatingProtection(FloatingProtectionMessage* msg, uint8_t* payload); 293 | void ParsePlatformInformation(PlatformInformationMessage* msg, uint8_t* rawData, uint32_t length); 294 | void ClearBuffer(uint8_t* buffer); 295 | uint8_t SerializeInt(int32_t value, uint8_t* serialized); 296 | void DecodeOctetString(uint8_t* rawData, uint32_t* position, uint32_t* destinationLength, uint8_t** destination); 297 | uint16_t GetLength(uint8_t* rawData); 298 | uint8_t GetNumLengthBytes(uint8_t* rawData); 299 | uint8_t buffer[BUFFER_SIZE]; 300 | int dataReady; 301 | int i2cAddress; 302 | uint16_t remainingRawLength; 303 | volatile MessageType lastSentMessage; 304 | TouchMetaInformation touchMetaInformation; 305 | bool touchDescriptorInitialized; 306 | }; 307 | 308 | extern Zforce zforce; 309 | --------------------------------------------------------------------------------