├── .gitignore ├── LICENSE ├── README.md └── hackeeg_driver ├── .gitignore ├── Base64.cpp ├── Base64.h ├── JsonCommand.cpp ├── JsonCommand.h ├── SerialCommand.cpp ├── SerialCommand.h ├── SpiDma.cpp ├── SpiDma.h ├── ads129x.h ├── adsCommand.cpp ├── adsCommand.h └── hackeeg_driver.ino /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | db.sqlite3 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # SageMath parsed files 84 | *.sage.py 85 | 86 | # Environments 87 | .env 88 | .venv 89 | env/ 90 | venv/ 91 | ENV/ 92 | env.bak/ 93 | venv.bak/ 94 | 95 | # Spyder project settings 96 | .spyderproject 97 | .spyproject 98 | 99 | # Rope project settings 100 | .ropeproject 101 | 102 | # mkdocs documentation 103 | /site 104 | 105 | # mypy 106 | .mypy_cache/ 107 | 108 | # swap 109 | [._]*.s[a-w][a-z] 110 | # auto-generated tag files 111 | tags 112 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2013 Adam Feuer 2 | 3 | This software is released under the GNU General Public License, version 4 | 3, or any later version. For more information, see: 5 | 6 | http://www.gnu.org/licenses/gpl.html 7 | 8 | . . . . . . . . . . . . . . . . . . . . . . . 9 | 10 | GNU GENERAL PUBLIC LICENSE 11 | Version 3, 29 June 2007 12 | 13 | Copyright (C) 2007 Free Software Foundation, Inc. 14 | Everyone is permitted to copy and distribute verbatim copies 15 | of this license document, but changing it is not allowed. 16 | 17 | Preamble 18 | 19 | The GNU General Public License is a free, copyleft license for 20 | software and other kinds of works. 21 | 22 | The licenses for most software and other practical works are designed 23 | to take away your freedom to share and change the works. By contrast, 24 | the GNU General Public License is intended to guarantee your freedom to 25 | share and change all versions of a program--to make sure it remains free 26 | software for all its users. We, the Free Software Foundation, use the 27 | GNU General Public License for most of our software; it applies also to 28 | any other work released this way by its authors. You can apply it to 29 | your programs, too. 30 | 31 | When we speak of free software, we are referring to freedom, not 32 | price. Our General Public Licenses are designed to make sure that you 33 | have the freedom to distribute copies of free software (and charge for 34 | them if you wish), that you receive source code or can get it if you 35 | want it, that you can change the software or use pieces of it in new 36 | free programs, and that you know you can do these things. 37 | 38 | To protect your rights, we need to prevent others from denying you 39 | these rights or asking you to surrender the rights. Therefore, you have 40 | certain responsibilities if you distribute copies of the software, or if 41 | you modify it: responsibilities to respect the freedom of others. 42 | 43 | For example, if you distribute copies of such a program, whether 44 | gratis or for a fee, you must pass on to the recipients the same 45 | freedoms that you received. You must make sure that they, too, receive 46 | or can get the source code. And you must show them these terms so they 47 | know their rights. 48 | 49 | Developers that use the GNU GPL protect your rights with two steps: 50 | (1) assert copyright on the software, and (2) offer you this License 51 | giving you legal permission to copy, distribute and/or modify it. 52 | 53 | For the developers' and authors' protection, the GPL clearly explains 54 | that there is no warranty for this free software. For both users' and 55 | authors' sake, the GPL requires that modified versions be marked as 56 | changed, so that their problems will not be attributed erroneously to 57 | authors of previous versions. 58 | 59 | Some devices are designed to deny users access to install or run 60 | modified versions of the software inside them, although the manufacturer 61 | can do so. This is fundamentally incompatible with the aim of 62 | protecting users' freedom to change the software. The systematic 63 | pattern of such abuse occurs in the area of products for individuals to 64 | use, which is precisely where it is most unacceptable. Therefore, we 65 | have designed this version of the GPL to prohibit the practice for those 66 | products. If such problems arise substantially in other domains, we 67 | stand ready to extend this provision to those domains in future versions 68 | of the GPL, as needed to protect the freedom of users. 69 | 70 | Finally, every program is threatened constantly by software patents. 71 | States should not allow patents to restrict development and use of 72 | software on general-purpose computers, but in those that do, we wish to 73 | avoid the special danger that patents applied to a free program could 74 | make it effectively proprietary. To prevent this, the GPL assures that 75 | patents cannot be used to render the program non-free. 76 | 77 | The precise terms and conditions for copying, distribution and 78 | modification follow. 79 | 80 | TERMS AND CONDITIONS 81 | 82 | 0. Definitions. 83 | 84 | "This License" refers to version 3 of the GNU General Public License. 85 | 86 | "Copyright" also means copyright-like laws that apply to other kinds of 87 | works, such as semiconductor masks. 88 | 89 | "The Program" refers to any copyrightable work licensed under this 90 | License. Each licensee is addressed as "you". "Licensees" and 91 | "recipients" may be individuals or organizations. 92 | 93 | To "modify" a work means to copy from or adapt all or part of the work 94 | in a fashion requiring copyright permission, other than the making of an 95 | exact copy. The resulting work is called a "modified version" of the 96 | earlier work or a work "based on" the earlier work. 97 | 98 | A "covered work" means either the unmodified Program or a work based 99 | on the Program. 100 | 101 | To "propagate" a work means to do anything with it that, without 102 | permission, would make you directly or secondarily liable for 103 | infringement under applicable copyright law, except executing it on a 104 | computer or modifying a private copy. Propagation includes copying, 105 | distribution (with or without modification), making available to the 106 | public, and in some countries other activities as well. 107 | 108 | To "convey" a work means any kind of propagation that enables other 109 | parties to make or receive copies. Mere interaction with a user through 110 | a computer network, with no transfer of a copy, is not conveying. 111 | 112 | An interactive user interface displays "Appropriate Legal Notices" 113 | to the extent that it includes a convenient and prominently visible 114 | feature that (1) displays an appropriate copyright notice, and (2) 115 | tells the user that there is no warranty for the work (except to the 116 | extent that warranties are provided), that licensees may convey the 117 | work under this License, and how to view a copy of this License. If 118 | the interface presents a list of user commands or options, such as a 119 | menu, a prominent item in the list meets this criterion. 120 | 121 | 1. Source Code. 122 | 123 | The "source code" for a work means the preferred form of the work 124 | for making modifications to it. "Object code" means any non-source 125 | form of a work. 126 | 127 | A "Standard Interface" means an interface that either is an official 128 | standard defined by a recognized standards body, or, in the case of 129 | interfaces specified for a particular programming language, one that 130 | is widely used among developers working in that language. 131 | 132 | The "System Libraries" of an executable work include anything, other 133 | than the work as a whole, that (a) is included in the normal form of 134 | packaging a Major Component, but which is not part of that Major 135 | Component, and (b) serves only to enable use of the work with that 136 | Major Component, or to implement a Standard Interface for which an 137 | implementation is available to the public in source code form. A 138 | "Major Component", in this context, means a major essential component 139 | (kernel, window system, and so on) of the specific operating system 140 | (if any) on which the executable work runs, or a compiler used to 141 | produce the work, or an object code interpreter used to run it. 142 | 143 | The "Corresponding Source" for a work in object code form means all 144 | the source code needed to generate, install, and (for an executable 145 | work) run the object code and to modify the work, including scripts to 146 | control those activities. However, it does not include the work's 147 | System Libraries, or general-purpose tools or generally available free 148 | programs which are used unmodified in performing those activities but 149 | which are not part of the work. For example, Corresponding Source 150 | includes interface definition files associated with source files for 151 | the work, and the source code for shared libraries and dynamically 152 | linked subprograms that the work is specifically designed to require, 153 | such as by intimate data communication or control flow between those 154 | subprograms and other parts of the work. 155 | 156 | The Corresponding Source need not include anything that users 157 | can regenerate automatically from other parts of the Corresponding 158 | Source. 159 | 160 | The Corresponding Source for a work in source code form is that 161 | same work. 162 | 163 | 2. Basic Permissions. 164 | 165 | All rights granted under this License are granted for the term of 166 | copyright on the Program, and are irrevocable provided the stated 167 | conditions are met. This License explicitly affirms your unlimited 168 | permission to run the unmodified Program. The output from running a 169 | covered work is covered by this License only if the output, given its 170 | content, constitutes a covered work. This License acknowledges your 171 | rights of fair use or other equivalent, as provided by copyright law. 172 | 173 | You may make, run and propagate covered works that you do not 174 | convey, without conditions so long as your license otherwise remains 175 | in force. You may convey covered works to others for the sole purpose 176 | of having them make modifications exclusively for you, or provide you 177 | with facilities for running those works, provided that you comply with 178 | the terms of this License in conveying all material for which you do 179 | not control copyright. Those thus making or running the covered works 180 | for you must do so exclusively on your behalf, under your direction 181 | and control, on terms that prohibit them from making any copies of 182 | your copyrighted material outside their relationship with you. 183 | 184 | Conveying under any other circumstances is permitted solely under 185 | the conditions stated below. Sublicensing is not allowed; section 10 186 | makes it unnecessary. 187 | 188 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 189 | 190 | No covered work shall be deemed part of an effective technological 191 | measure under any applicable law fulfilling obligations under article 192 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 193 | similar laws prohibiting or restricting circumvention of such 194 | measures. 195 | 196 | When you convey a covered work, you waive any legal power to forbid 197 | circumvention of technological measures to the extent such circumvention 198 | is effected by exercising rights under this License with respect to 199 | the covered work, and you disclaim any intention to limit operation or 200 | modification of the work as a means of enforcing, against the work's 201 | users, your or third parties' legal rights to forbid circumvention of 202 | technological measures. 203 | 204 | 4. Conveying Verbatim Copies. 205 | 206 | You may convey verbatim copies of the Program's source code as you 207 | receive it, in any medium, provided that you conspicuously and 208 | appropriately publish on each copy an appropriate copyright notice; 209 | keep intact all notices stating that this License and any 210 | non-permissive terms added in accord with section 7 apply to the code; 211 | keep intact all notices of the absence of any warranty; and give all 212 | recipients a copy of this License along with the Program. 213 | 214 | You may charge any price or no price for each copy that you convey, 215 | and you may offer support or warranty protection for a fee. 216 | 217 | 5. Conveying Modified Source Versions. 218 | 219 | You may convey a work based on the Program, or the modifications to 220 | produce it from the Program, in the form of source code under the 221 | terms of section 4, provided that you also meet all of these conditions: 222 | 223 | a) The work must carry prominent notices stating that you modified 224 | it, and giving a relevant date. 225 | 226 | b) The work must carry prominent notices stating that it is 227 | released under this License and any conditions added under section 228 | 7. This requirement modifies the requirement in section 4 to 229 | "keep intact all notices". 230 | 231 | c) You must license the entire work, as a whole, under this 232 | License to anyone who comes into possession of a copy. This 233 | License will therefore apply, along with any applicable section 7 234 | additional terms, to the whole of the work, and all its parts, 235 | regardless of how they are packaged. This License gives no 236 | permission to license the work in any other way, but it does not 237 | invalidate such permission if you have separately received it. 238 | 239 | d) If the work has interactive user interfaces, each must display 240 | Appropriate Legal Notices; however, if the Program has interactive 241 | interfaces that do not display Appropriate Legal Notices, your 242 | work need not make them do so. 243 | 244 | A compilation of a covered work with other separate and independent 245 | works, which are not by their nature extensions of the covered work, 246 | and which are not combined with it such as to form a larger program, 247 | in or on a volume of a storage or distribution medium, is called an 248 | "aggregate" if the compilation and its resulting copyright are not 249 | used to limit the access or legal rights of the compilation's users 250 | beyond what the individual works permit. Inclusion of a covered work 251 | in an aggregate does not cause this License to apply to the other 252 | parts of the aggregate. 253 | 254 | 6. Conveying Non-Source Forms. 255 | 256 | You may convey a covered work in object code form under the terms 257 | of sections 4 and 5, provided that you also convey the 258 | machine-readable Corresponding Source under the terms of this License, 259 | in one of these ways: 260 | 261 | a) Convey the object code in, or embodied in, a physical product 262 | (including a physical distribution medium), accompanied by the 263 | Corresponding Source fixed on a durable physical medium 264 | customarily used for software interchange. 265 | 266 | b) Convey the object code in, or embodied in, a physical product 267 | (including a physical distribution medium), accompanied by a 268 | written offer, valid for at least three years and valid for as 269 | long as you offer spare parts or customer support for that product 270 | model, to give anyone who possesses the object code either (1) a 271 | copy of the Corresponding Source for all the software in the 272 | product that is covered by this License, on a durable physical 273 | medium customarily used for software interchange, for a price no 274 | more than your reasonable cost of physically performing this 275 | conveying of source, or (2) access to copy the 276 | Corresponding Source from a network server at no charge. 277 | 278 | c) Convey individual copies of the object code with a copy of the 279 | written offer to provide the Corresponding Source. This 280 | alternative is allowed only occasionally and noncommercially, and 281 | only if you received the object code with such an offer, in accord 282 | with subsection 6b. 283 | 284 | d) Convey the object code by offering access from a designated 285 | place (gratis or for a charge), and offer equivalent access to the 286 | Corresponding Source in the same way through the same place at no 287 | further charge. You need not require recipients to copy the 288 | Corresponding Source along with the object code. If the place to 289 | copy the object code is a network server, the Corresponding Source 290 | may be on a different server (operated by you or a third party) 291 | that supports equivalent copying facilities, provided you maintain 292 | clear directions next to the object code saying where to find the 293 | Corresponding Source. Regardless of what server hosts the 294 | Corresponding Source, you remain obligated to ensure that it is 295 | available for as long as needed to satisfy these requirements. 296 | 297 | e) Convey the object code using peer-to-peer transmission, provided 298 | you inform other peers where the object code and Corresponding 299 | Source of the work are being offered to the general public at no 300 | charge under subsection 6d. 301 | 302 | A separable portion of the object code, whose source code is excluded 303 | from the Corresponding Source as a System Library, need not be 304 | included in conveying the object code work. 305 | 306 | A "User Product" is either (1) a "consumer product", which means any 307 | tangible personal property which is normally used for personal, family, 308 | or household purposes, or (2) anything designed or sold for incorporation 309 | into a dwelling. In determining whether a product is a consumer product, 310 | doubtful cases shall be resolved in favor of coverage. For a particular 311 | product received by a particular user, "normally used" refers to a 312 | typical or common use of that class of product, regardless of the status 313 | of the particular user or of the way in which the particular user 314 | actually uses, or expects or is expected to use, the product. A product 315 | is a consumer product regardless of whether the product has substantial 316 | commercial, industrial or non-consumer uses, unless such uses represent 317 | the only significant mode of use of the product. 318 | 319 | "Installation Information" for a User Product means any methods, 320 | procedures, authorization keys, or other information required to install 321 | and execute modified versions of a covered work in that User Product from 322 | a modified version of its Corresponding Source. The information must 323 | suffice to ensure that the continued functioning of the modified object 324 | code is in no case prevented or interfered with solely because 325 | modification has been made. 326 | 327 | If you convey an object code work under this section in, or with, or 328 | specifically for use in, a User Product, and the conveying occurs as 329 | part of a transaction in which the right of possession and use of the 330 | User Product is transferred to the recipient in perpetuity or for a 331 | fixed term (regardless of how the transaction is characterized), the 332 | Corresponding Source conveyed under this section must be accompanied 333 | by the Installation Information. But this requirement does not apply 334 | if neither you nor any third party retains the ability to install 335 | modified object code on the User Product (for example, the work has 336 | been installed in ROM). 337 | 338 | The requirement to provide Installation Information does not include a 339 | requirement to continue to provide support service, warranty, or updates 340 | for a work that has been modified or installed by the recipient, or for 341 | the User Product in which it has been modified or installed. Access to a 342 | network may be denied when the modification itself materially and 343 | adversely affects the operation of the network or violates the rules and 344 | protocols for communication across the network. 345 | 346 | Corresponding Source conveyed, and Installation Information provided, 347 | in accord with this section must be in a format that is publicly 348 | documented (and with an implementation available to the public in 349 | source code form), and must require no special password or key for 350 | unpacking, reading or copying. 351 | 352 | 7. Additional Terms. 353 | 354 | "Additional permissions" are terms that supplement the terms of this 355 | License by making exceptions from one or more of its conditions. 356 | Additional permissions that are applicable to the entire Program shall 357 | be treated as though they were included in this License, to the extent 358 | that they are valid under applicable law. If additional permissions 359 | apply only to part of the Program, that part may be used separately 360 | under those permissions, but the entire Program remains governed by 361 | this License without regard to the additional permissions. 362 | 363 | When you convey a copy of a covered work, you may at your option 364 | remove any additional permissions from that copy, or from any part of 365 | it. (Additional permissions may be written to require their own 366 | removal in certain cases when you modify the work.) You may place 367 | additional permissions on material, added by you to a covered work, 368 | for which you have or can give appropriate copyright permission. 369 | 370 | Notwithstanding any other provision of this License, for material you 371 | add to a covered work, you may (if authorized by the copyright holders of 372 | that material) supplement the terms of this License with terms: 373 | 374 | a) Disclaiming warranty or limiting liability differently from the 375 | terms of sections 15 and 16 of this License; or 376 | 377 | b) Requiring preservation of specified reasonable legal notices or 378 | author attributions in that material or in the Appropriate Legal 379 | Notices displayed by works containing it; or 380 | 381 | c) Prohibiting misrepresentation of the origin of that material, or 382 | requiring that modified versions of such material be marked in 383 | reasonable ways as different from the original version; or 384 | 385 | d) Limiting the use for publicity purposes of names of licensors or 386 | authors of the material; or 387 | 388 | e) Declining to grant rights under trademark law for use of some 389 | trade names, trademarks, or service marks; or 390 | 391 | f) Requiring indemnification of licensors and authors of that 392 | material by anyone who conveys the material (or modified versions of 393 | it) with contractual assumptions of liability to the recipient, for 394 | any liability that these contractual assumptions directly impose on 395 | those licensors and authors. 396 | 397 | All other non-permissive additional terms are considered "further 398 | restrictions" within the meaning of section 10. If the Program as you 399 | received it, or any part of it, contains a notice stating that it is 400 | governed by this License along with a term that is a further 401 | restriction, you may remove that term. If a license document contains 402 | a further restriction but permits relicensing or conveying under this 403 | License, you may add to a covered work material governed by the terms 404 | of that license document, provided that the further restriction does 405 | not survive such relicensing or conveying. 406 | 407 | If you add terms to a covered work in accord with this section, you 408 | must place, in the relevant source files, a statement of the 409 | additional terms that apply to those files, or a notice indicating 410 | where to find the applicable terms. 411 | 412 | Additional terms, permissive or non-permissive, may be stated in the 413 | form of a separately written license, or stated as exceptions; 414 | the above requirements apply either way. 415 | 416 | 8. Termination. 417 | 418 | You may not propagate or modify a covered work except as expressly 419 | provided under this License. Any attempt otherwise to propagate or 420 | modify it is void, and will automatically terminate your rights under 421 | this License (including any patent licenses granted under the third 422 | paragraph of section 11). 423 | 424 | However, if you cease all violation of this License, then your 425 | license from a particular copyright holder is reinstated (a) 426 | provisionally, unless and until the copyright holder explicitly and 427 | finally terminates your license, and (b) permanently, if the copyright 428 | holder fails to notify you of the violation by some reasonable means 429 | prior to 60 days after the cessation. 430 | 431 | Moreover, your license from a particular copyright holder is 432 | reinstated permanently if the copyright holder notifies you of the 433 | violation by some reasonable means, this is the first time you have 434 | received notice of violation of this License (for any work) from that 435 | copyright holder, and you cure the violation prior to 30 days after 436 | your receipt of the notice. 437 | 438 | Termination of your rights under this section does not terminate the 439 | licenses of parties who have received copies or rights from you under 440 | this License. If your rights have been terminated and not permanently 441 | reinstated, you do not qualify to receive new licenses for the same 442 | material under section 10. 443 | 444 | 9. Acceptance Not Required for Having Copies. 445 | 446 | You are not required to accept this License in order to receive or 447 | run a copy of the Program. Ancillary propagation of a covered work 448 | occurring solely as a consequence of using peer-to-peer transmission 449 | to receive a copy likewise does not require acceptance. However, 450 | nothing other than this License grants you permission to propagate or 451 | modify any covered work. These actions infringe copyright if you do 452 | not accept this License. Therefore, by modifying or propagating a 453 | covered work, you indicate your acceptance of this License to do so. 454 | 455 | 10. Automatic Licensing of Downstream Recipients. 456 | 457 | Each time you convey a covered work, the recipient automatically 458 | receives a license from the original licensors, to run, modify and 459 | propagate that work, subject to this License. You are not responsible 460 | for enforcing compliance by third parties with this License. 461 | 462 | An "entity transaction" is a transaction transferring control of an 463 | organization, or substantially all assets of one, or subdividing an 464 | organization, or merging organizations. If propagation of a covered 465 | work results from an entity transaction, each party to that 466 | transaction who receives a copy of the work also receives whatever 467 | licenses to the work the party's predecessor in interest had or could 468 | give under the previous paragraph, plus a right to possession of the 469 | Corresponding Source of the work from the predecessor in interest, if 470 | the predecessor has it or can get it with reasonable efforts. 471 | 472 | You may not impose any further restrictions on the exercise of the 473 | rights granted or affirmed under this License. For example, you may 474 | not impose a license fee, royalty, or other charge for exercise of 475 | rights granted under this License, and you may not initiate litigation 476 | (including a cross-claim or counterclaim in a lawsuit) alleging that 477 | any patent claim is infringed by making, using, selling, offering for 478 | sale, or importing the Program or any portion of it. 479 | 480 | 11. Patents. 481 | 482 | A "contributor" is a copyright holder who authorizes use under this 483 | License of the Program or a work on which the Program is based. The 484 | work thus licensed is called the contributor's "contributor version". 485 | 486 | A contributor's "essential patent claims" are all patent claims 487 | owned or controlled by the contributor, whether already acquired or 488 | hereafter acquired, that would be infringed by some manner, permitted 489 | by this License, of making, using, or selling its contributor version, 490 | but do not include claims that would be infringed only as a 491 | consequence of further modification of the contributor version. For 492 | purposes of this definition, "control" includes the right to grant 493 | patent sublicenses in a manner consistent with the requirements of 494 | this License. 495 | 496 | Each contributor grants you a non-exclusive, worldwide, royalty-free 497 | patent license under the contributor's essential patent claims, to 498 | make, use, sell, offer for sale, import and otherwise run, modify and 499 | propagate the contents of its contributor version. 500 | 501 | In the following three paragraphs, a "patent license" is any express 502 | agreement or commitment, however denominated, not to enforce a patent 503 | (such as an express permission to practice a patent or covenant not to 504 | sue for patent infringement). To "grant" such a patent license to a 505 | party means to make such an agreement or commitment not to enforce a 506 | patent against the party. 507 | 508 | If you convey a covered work, knowingly relying on a patent license, 509 | and the Corresponding Source of the work is not available for anyone 510 | to copy, free of charge and under the terms of this License, through a 511 | publicly available network server or other readily accessible means, 512 | then you must either (1) cause the Corresponding Source to be so 513 | available, or (2) arrange to deprive yourself of the benefit of the 514 | patent license for this particular work, or (3) arrange, in a manner 515 | consistent with the requirements of this License, to extend the patent 516 | license to downstream recipients. "Knowingly relying" means you have 517 | actual knowledge that, but for the patent license, your conveying the 518 | covered work in a country, or your recipient's use of the covered work 519 | in a country, would infringe one or more identifiable patents in that 520 | country that you have reason to believe are valid. 521 | 522 | If, pursuant to or in connection with a single transaction or 523 | arrangement, you convey, or propagate by procuring conveyance of, a 524 | covered work, and grant a patent license to some of the parties 525 | receiving the covered work authorizing them to use, propagate, modify 526 | or convey a specific copy of the covered work, then the patent license 527 | you grant is automatically extended to all recipients of the covered 528 | work and works based on it. 529 | 530 | A patent license is "discriminatory" if it does not include within 531 | the scope of its coverage, prohibits the exercise of, or is 532 | conditioned on the non-exercise of one or more of the rights that are 533 | specifically granted under this License. You may not convey a covered 534 | work if you are a party to an arrangement with a third party that is 535 | in the business of distributing software, under which you make payment 536 | to the third party based on the extent of your activity of conveying 537 | the work, and under which the third party grants, to any of the 538 | parties who would receive the covered work from you, a discriminatory 539 | patent license (a) in connection with copies of the covered work 540 | conveyed by you (or copies made from those copies), or (b) primarily 541 | for and in connection with specific products or compilations that 542 | contain the covered work, unless you entered into that arrangement, 543 | or that patent license was granted, prior to 28 March 2007. 544 | 545 | Nothing in this License shall be construed as excluding or limiting 546 | any implied license or other defenses to infringement that may 547 | otherwise be available to you under applicable patent law. 548 | 549 | 12. No Surrender of Others' Freedom. 550 | 551 | If conditions are imposed on you (whether by court order, agreement or 552 | otherwise) that contradict the conditions of this License, they do not 553 | excuse you from the conditions of this License. If you cannot convey a 554 | covered work so as to satisfy simultaneously your obligations under this 555 | License and any other pertinent obligations, then as a consequence you may 556 | not convey it at all. For example, if you agree to terms that obligate you 557 | to collect a royalty for further conveying from those to whom you convey 558 | the Program, the only way you could satisfy both those terms and this 559 | License would be to refrain entirely from conveying the Program. 560 | 561 | 13. Use with the GNU Affero General Public License. 562 | 563 | Notwithstanding any other provision of this License, you have 564 | permission to link or combine any covered work with a work licensed 565 | under version 3 of the GNU Affero General Public License into a single 566 | combined work, and to convey the resulting work. The terms of this 567 | License will continue to apply to the part which is the covered work, 568 | but the special requirements of the GNU Affero General Public License, 569 | section 13, concerning interaction through a network will apply to the 570 | combination as such. 571 | 572 | 14. Revised Versions of this License. 573 | 574 | The Free Software Foundation may publish revised and/or new versions of 575 | the GNU General Public License from time to time. Such new versions will 576 | be similar in spirit to the present version, but may differ in detail to 577 | address new problems or concerns. 578 | 579 | Each version is given a distinguishing version number. If the 580 | Program specifies that a certain numbered version of the GNU General 581 | Public License "or any later version" applies to it, you have the 582 | option of following the terms and conditions either of that numbered 583 | version or of any later version published by the Free Software 584 | Foundation. If the Program does not specify a version number of the 585 | GNU General Public License, you may choose any version ever published 586 | by the Free Software Foundation. 587 | 588 | If the Program specifies that a proxy can decide which future 589 | versions of the GNU General Public License can be used, that proxy's 590 | public statement of acceptance of a version permanently authorizes you 591 | to choose that version for the Program. 592 | 593 | Later license versions may give you additional or different 594 | permissions. However, no additional obligations are imposed on any 595 | author or copyright holder as a result of your choosing to follow a 596 | later version. 597 | 598 | 15. Disclaimer of Warranty. 599 | 600 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 601 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 602 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 603 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 604 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 605 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 606 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 607 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 608 | 609 | 16. Limitation of Liability. 610 | 611 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 612 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 613 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 614 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 615 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 616 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 617 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 618 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 619 | SUCH DAMAGES. 620 | 621 | 17. Interpretation of Sections 15 and 16. 622 | 623 | If the disclaimer of warranty and limitation of liability provided 624 | above cannot be given local legal effect according to their terms, 625 | reviewing courts shall apply local law that most closely approximates 626 | an absolute waiver of all civil liability in connection with the 627 | Program, unless a warranty or assumption of liability accompanies a 628 | copy of the Program in return for a fee. 629 | 630 | END OF TERMS AND CONDITIONS 631 | 632 | How to Apply These Terms to Your New Programs 633 | 634 | If you develop a new program, and you want it to be of the greatest 635 | possible use to the public, the best way to achieve this is to make it 636 | free software which everyone can redistribute and change under these terms. 637 | 638 | To do so, attach the following notices to the program. It is safest 639 | to attach them to the start of each source file to most effectively 640 | state the exclusion of warranty; and each file should have at least 641 | the "copyright" line and a pointer to where the full notice is found. 642 | 643 | 644 | Copyright (C) 645 | 646 | This program is free software: you can redistribute it and/or modify 647 | it under the terms of the GNU General Public License as published by 648 | the Free Software Foundation, either version 3 of the License, or 649 | (at your option) any later version. 650 | 651 | This program is distributed in the hope that it will be useful, 652 | but WITHOUT ANY WARRANTY; without even the implied warranty of 653 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 654 | GNU General Public License for more details. 655 | 656 | You should have received a copy of the GNU General Public License 657 | along with this program. If not, see . 658 | 659 | Also add information on how to contact you by electronic and paper mail. 660 | 661 | If the program does terminal interaction, make it output a short 662 | notice like this when it starts in an interactive mode: 663 | 664 | Copyright (C) 665 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 666 | This is free software, and you are welcome to redistribute it 667 | under certain conditions; type `show c' for details. 668 | 669 | The hypothetical commands `show w' and `show c' should show the appropriate 670 | parts of the General Public License. Of course, your program's commands 671 | might be different; for a GUI interface, you would use an "about box". 672 | 673 | You should also get your employer (if you work as a programmer) or school, 674 | if any, to sign a "copyright disclaimer" for the program, if necessary. 675 | For more information on this, and how to apply and follow the GNU GPL, see 676 | . 677 | 678 | The GNU General Public License does not permit incorporating your program 679 | into proprietary programs. If your program is a subroutine library, you 680 | may consider it more useful to permit linking proprietary applications with 681 | the library. If this is what you want to do, use the GNU Lesser General 682 | Public License instead of this License. But first, please read 683 | . 684 | 685 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HackEEG Arduino Driver 2 | 3 | This is the Arduino driver code for the [HackEEG Arduino Due shield](https://github.com/starcat-io/hackeeg-shield) for the [TI ADS1299](http://www.ti.com/product/ads1299) EEG system-on-a-chip. 4 | 5 | The [TI ADS1299](http://www.ti.com/product/ads1299) is a 24-bit 8-channel ADC meant specifically for EEG, with 24x programmable gain amplifiers and much of the analog circuitry needed for EEG. It is capable of digitizing 16,000 samples per second at 24 bit resolution. The ADS1299-4 is a 4-channel version of the ADS1299; the ADS1299-6 is a 6-channel version. 6 | 7 | ## Arduino drivers 8 | 9 | The src/ directory contains an Arduino sketch and associated C/C++ files that make up a driver for ADS129x chips. So far it has only been tested on the ADS1299, but should work on the other models. This driver has been tested on the Arduino Due and Mega2560, but should also work on other Arduinos. The DMA mode can only be used on the Arduino Due. 10 | 11 | The driver has a text-mode interface, so can be used without any client software – just open up a serial port to the SAM3X8E native USB port (line endings NL+CR). It also has a JSONLines mode for easy parsing by client programs and a MessagePack mode for efficient binary communication. 12 | 13 | In MessagePack mode, using the Arduino Due's native SPI DMA, driver can read from the ADS1299 at 16,000 samples per second, and can send that data on to the host via the Arduino Due's USB 2.0 High Speed connection at the same rate. 14 | 15 | By default the driver uses the Arduino library's software SPI (without DMA), and can read and send 8,000 samples per second in that configuration on the Arduino Due, either in JSON Lines mode or MessagePack mode. Other Arduinos will have lower performance. 16 | 17 | When in MessagePack mode, MessagePack format is only used to transfer data in `rdata` and `rdatac` commands; all other communication takes place by JSON Lines. 18 | 19 | In text mode, samples are encoded using the [base64](http://en.wikipedia.org/wiki/Base64) encoding by default. 20 | 21 | 22 | ## Installing 23 | 24 | You must install the [ArduinoJson](https://arduinojson.org/v6/doc/installation/) library before compiling this driver. 25 | 26 | 27 | ## Using the Driver 28 | 29 | The commands that are available are: 30 | 31 | * RREG – read register. Takes two hex digits as an argument (one byte, for example: FF). Argument 1 is the register to read. 32 | Returns a single hex-encoded byte (for example, 0E) that represents the contents of the register. 33 | * WREG – write register. Takes two hex-encoded bytes as arguments, separated by a space. Argument 1 is the register to write, argument 2 is the register value. 34 | * RDATA – read one sample of data from the ADS129x chip. Returns 3 bytes of header, plus 3 bytes x number of channels (8 for ADS1298 or ADS1299), encoded using base64 or packed hex in text mode, and JSON Lines byte array, or MessagePack byte array, depending on the protocol 35 | * RDATAC – start read data continuous mode. Data is read into a buffer in the Arduino RAM, and streamed to the client one sample at a time, either in packed base64 format or packed hex format in text, JSON Lines byte array, or MessagePack byte array, depending on the protocol. 36 | * SDATAC – stop read data continuous mode. 37 | * VERSION – reports the driver version 38 | * SERIALNUMBER – reports the HackEEG serial number (UUID from the onboard 24AA256UID-I/SN I2S EEPROM) 39 | * LEDON – turns on the Arduino Due onboard LED. 40 | * LEDOFF – turns off the Arduino Due onboard LED. 41 | * BOARDLEDON – turns on the HackEEG Shield LED. (HackEEG shield has a blue LED connected to ADS1299 GPIO4) 42 | * BOARDLEDOFF – turns off the HackEEG Shield LED. 43 | * BASE64 – RDATA/RDATAC commands will encode data in base64. 44 | * TEXT – communication protocol switches to text. See the Communication Protocol section. 45 | * JSONLINES – communication protocol switches from text to [JSONLines](http://jsonlines.org/). This is a text-oriented serialization format with libraries in many languages. See the Communication Protocol section. 46 | * MESSAGEPACK – communication protocol switches from text to [MessagePack](https://msgpack.org) for `rdatac` data only. This is a concise binary serialization format with libraries in many languages. See the Communication Protocol section. 47 | * HEX – RDATA commands will encode data in hexidecimal format. 48 | * HELP – prints a list of available commands. 49 | 50 | 51 | 52 | ## General Operation 53 | 54 | The ADS129x chips are configured by reading and writing registers. See the chip datasheet for more information about configuring the ADS129x and reading data from it. 55 | 56 | If the host program (the program that reads data from the driver) does not pull data from the serial or USB interface fast enough, the driver will block on sending when the serial or USB buffers fill up. This will cause the driver to lose samples. 57 | 58 | The driver uses the Arduino Native port for serial communication, because it is capable of 2 megabits per second or more. 59 | 60 | 61 | In most applications, the Python 3 usage will go something like this: 62 | 63 | ```python 64 | #!/usr/bin/env python 65 | 66 | SERIAL_PORT_PATH="/dev/cu.usbmodem14434401" # your actual path to the Arduino Native serial port device goes here 67 | import sys 68 | import hackeeg 69 | from hackeeg import ads1299 70 | 71 | hackeeg = hackeeg.HackEEGBoard(SERIAL_PORT_PATH) 72 | hackeeg.connect() 73 | hackeeg.sdatac() 74 | hackeeg.reset() 75 | hackeeg.blink_board_led() 76 | hackeeg.disable_all_channels() 77 | sample_mode = ads1299.HIGH_RES_250_SPS | ads1299.CONFIG1_const 78 | hackeeg.wreg(ads1299.CONFIG1, sample_mode) 79 | test_signal_mode = ads1299.INT_TEST_4HZ | ads1299.CONFIG2_const 80 | hackeeg.wreg(ads1299.CONFIG2, test_signal_mode) 81 | hackeeg.enable_channel(7) 82 | hackeeg.wreg(ads1299.CH7SET, ads1299.TEST_SIGNAL | ads1299.GAIN_1X) 83 | hackeeg.rreg(ads1299.CH5SET) 84 | 85 | # Unipolar mode - setting SRB1 bit sends mid-supply voltage to the N inputs 86 | hackeeg.wreg(ads1299.MISC1, ads1299.SRB1) 87 | # add channels into bias generation 88 | hackeeg.wreg(ads1299.BIAS_SENSP, ads1299.BIAS8P) 89 | hackeeg.rdatac() 90 | hackeeg.start() 91 | 92 | while True: 93 | result = hackeeg.read_response() 94 | status_code = result.get('STATUS_CODE') 95 | status_text = result.get('STATUS_TEXT') 96 | data = result.get(hackeeg.DataKey) 97 | if data: 98 | decoded_data = result.get(hackeeg.DecodedDataKey) 99 | if decoded_data: 100 | timestamp = decoded_data.get('timestamp') 101 | ads_gpio = decoded_data.get('ads_gpio') 102 | loff_statp = decoded_data.get('loff_statp') 103 | loff_statn = decoded_data.get('loff_statn') 104 | channel_data = decoded_data.get('channel_data') 105 | print(f"timestamp:{timestamp} | gpio:{ads_gpio} loff_statp:{loff_statp} loff_statn:{loff_statn} | ", 106 | end='') 107 | for channel_number, sample in enumerate(channel_data): 108 | print(f"{channel_number + 1}:{sample} ", end='') 109 | print() 110 | else: 111 | print(data) 112 | sys.stdout.flush() 113 | ``` 114 | 115 | ## SPI DMA 116 | 117 | The driver running on the Arduino communicates with the ADS1299 chip via the SPI interface. For data rates from 250 to 8,192 samples per second, the driver can use the Arduino API's built in software SPI. This is simplest. 118 | 119 | To use the 16,384 samples per second data rate, or to reduce CPU load on the Arduino Due, the driver can use the Arduino Due's [Atmel SAM3X8E](https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf) CPU's built-in SPI DMA ([Direct Memory Access](https://en.wikipedia.org/wiki/Direct_memory_access)) controller. Using the SPI DMA offloads all SPI communication to the SAM3X8E DMA controller, freeing up the CPU to do other tasks like serial communication. 120 | 121 | To enable this, change the following constants in `SpiDma.cpp` from this: 122 | 123 | Arduino software SPI: 124 | 125 | ``` 126 | #define USE_ARDUINO_SPI_LIBRARY 1 127 | #define USE_NATIVE_SAM3X_SPI 0 128 | ``` 129 | 130 | To this (HackEEG SPI DMA): 131 | 132 | ``` 133 | #define USE_ARDUINO_SPI_LIBRARY 0 134 | #define USE_NATIVE_SAM3X_SPI 1 135 | ``` 136 | 137 | Then recompile and upload the driver sketch to the Arduino. 138 | 139 | 140 | 141 | ## Communication Protocol 142 | 143 | ### Text mode 144 | 145 | The driver starts up in this mode. This mode is easiest to use when quick communication with the board is needed without a client program. 146 | 147 | When the command TEXT is given, the driver communication protocol switches to text, and the response will be in text format. Commands can be given in lower or upper case, parameters separated by a space. Responses are given on one line, starting with a status code (200 for OK, errors are in the 300s and 400s.) Commands can be given in upper or lower case. 148 | 149 | ### JSON Lines mode 150 | 151 | Give the JSONLINES command to switch to this mode. 152 | 153 | When the command JSONLINES is given, the driver communication protocol switches to [JSON Lines](http://jsonlines.org/) format, and the response will be in JSON Lines format. Commands and Responses are a single map, with keys and values determine the command and parameters. The format is as follows (on separate lines for readability; in use, the entire JSON blob would be on its own line): 154 | 155 | #### Commands: 156 | 157 | ``` 158 | { 159 | COMMAND: "", 160 | PARAMETERS: [ , , ... ] 161 | } 162 | ``` 163 | 164 | #### Responses: 165 | 166 | ``` 167 | { 168 | STATUS_CODE: , 169 | STATUS_TEXT: "", 170 | HEADERS: ["header1", "header2", ... ], 171 | DATA: [value1, value2, value3, ... ] 172 | } 173 | ``` 174 | 175 | Here is an example exchange: 176 | 177 | ``` 178 | {"COMMAND" : "boardledon"} 179 | {"STATUS_CODE" : 200, "STATUS_TEXT": "Ok" } 180 | 181 | ``` 182 | 183 | Headers are optional and may or may not be provided. 184 | 185 | #### JSON Lines concise mode for `rdata` and `rdatc` responses 186 | 187 | For `rdata` and `rdatac` responses, in order to send data at high speeds, a special response format is used that is similar to the MessagePack format: 188 | 189 | ``` 190 | { 191 | C: , 192 | D: "" 193 | } 194 | ``` 195 | 196 | In concise JSON Lines mode, status text is omitted. The data is sent as a base64-encoded byte array to minimize transformation of the data into human-readable form. This allows faster transfer. See below for the format of the decoded data byte-array. 197 | 198 | #### Software library 199 | 200 | The Arduino driver uses the [ArduinoJson](https://arduinojson.org/) library for encoding and decoding JSON Lines data, except for `rdata` and `rdatac` messages– those use a custom, optimized sending routine. 201 | 202 | 203 | ### MessagePack mode 204 | 205 | Give the MESSAGEPACK command to switch to this mode. 206 | 207 | When the command MESSAGEPACK is given, the driver communication protocol for `rdata` and `rdatac` data packets ONLY switches to [MessagePack](https://msgpack.org) concise binary format. Commands are still given in JSON Lines format, and responses for all commands other than `rdata` and `rdatac` will be in JSON Lines. Responses for `rdata` and `rdatac` will be in MessagePack format. This mode is available to improve data transfer speed since the binary data from the SPI interface can be transferred as-is with no copying or transformation. 208 | 209 | The format is as follows (on separate lines as JSON for readability, in use this would be packed as a binary structure): 210 | 211 | #### Responses: 212 | 213 | ``` 214 | { 215 | C: , 216 | D: 217 | } 218 | ``` 219 | 220 | In MessagePack mode, status text is omitted. 221 | 222 | 223 | #### Software library 224 | 225 | The Arduino driver uses the [ArduinoJson](https://arduinojson.org/) library for encoding and decoding MessagePack data, except for `rdata` and `rdatac` messages– those use a custom, optimized sending routine. 226 | 227 | 228 | ### Byte-array Format 229 | 230 | The packed byte-array used for `rdata` and `rdatac` transfers has this format: 231 | 232 | | position | function | byte | 233 | |----------|---------------|------| 234 | | 00 | timestamp | 0 | 235 | | 01 | timestamp | 1 | 236 | | 02 | timestamp | 2 | 237 | | 03 | timestamp | 3 | 238 | | 04 | sample number | 0 | 239 | | 05 | sample number | 1 | 240 | | 06 | sample number | 2 | 241 | | 07 | sample number | 3 | 242 | | 08 | channel 1 | 0 | 243 | | 09 | channel 1 | 1 | 244 | | 10 | channel 1 | 2 | 245 | | 11 | channel 2 | 0 | 246 | | 12 | channel 2 | 1 | 247 | | 13 | channel 2 | 2 | 248 | | 14 | channel 3 | 0 | 249 | | 15 | channel 3 | 1 | 250 | | 16 | channel 3 | 2 | 251 | | 17 | channel 4 | 0 | 252 | | 18 | channel 4 | 1 | 253 | | 19 | channel 4 | 2 | 254 | | 20 | channel 5 | 0 | 255 | | 21 | channel 5 | 1 | 256 | | 22 | channel 5 | 2 | 257 | | 23 | channel 6 | 0 | 258 | | 24 | channel 6 | 1 | 259 | | 25 | channel 6 | 2 | 260 | | 26 | channel 7 | 0 | 261 | | 27 | channel 7 | 1 | 262 | | 28 | channel 7 | 2 | 263 | | 29 | channel 8 | 0 | 264 | | 30 | channel 8 | 1 | 265 | | 31 | channel 8 | 2 | 266 | 267 | * `timestamp` is an unsigned long, the result of calling the Arduino's `micros()` function right before the sample is taken. 268 | * `sample number` is an unsigned long, the value is incremented for every sample received by the driver. It is reset every time you issue the `start` command. You can analyze the sample number sequence on the client to see if your client code is dropping or missing samples. 269 | * For an example Python decoding function, see [`_decode_data()`](https://github.com/adamfeuer/ADS129x-tools/blob/master/ads129x_client/hackeeg/driver.py#L123-L152) in the file `hackeeg/driver.py`. 270 | 271 | 272 | ## HackEEG Python Client Software 273 | 274 | There is [HackEEG Python client software](https://github.com/starcat-io/hackeeg-client-python) that can run on macOS, Linux, and Windows, and stream data 275 | to [Lab Streaming Layer](https://github.com/sccn/labstreaminglayer) at 16,000 samples per second. 276 | 277 | ## Hardware 278 | 279 | If you are looking for a full Arduino shield with analog input capability, you might be interested in the 280 | [HackEEG Shield](https://github.com/adamfeuer/hackeeg-shield). The HackEEG shield is designed for use with the ADS1299. 281 | 282 | ## Credits 283 | 284 | This software would not be possible without the help of many people: 285 | 286 | * Kendrick Shaw, Ace Medlock, and Eric Herman (parts of the ADS129x.h header file and some parts of the ADS129x driver, 287 | * see [OpenHardwareExG project](https://github.com/OpenElectronicsLab/OpenHardwareExG) for more info. 288 | * Chris Rorden (some parts of the ADS1298 driver) 289 | * Stefan Rado (SerialCommand library) 290 | * Steven Cogswell (SerialCommand library) 291 | * William Greiman (SPI DMA library) 292 | * Cristian Maglie (SPI DMA library) 293 | * Bill Porter (SPI DMA library) 294 | * Adam Rudd ([Base64](https://github.com/adamvr/arduino-base64) library) 295 | * Benoît Blanchon ([ArduinoJson](https://arduinojson.org/) library) 296 | 297 | 298 | If I forgot to credit you, please let me know! 299 | 300 | If you have questions, comments, or improvements, I would love to know them! Pull requests welcome! 301 | 302 | cheers
303 | adam
304 |
305 | Adam Feuer
306 | adam@starcat.io
307 | [Starcat LLC](https://starcat.io)
308 | Seattle, WA, USA
309 | -------------------------------------------------------------------------------- /hackeeg_driver/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # IDEs 3 | .idea/ 4 | 5 | # binaries 6 | *.elf 7 | *.bin 8 | -------------------------------------------------------------------------------- /hackeeg_driver/Base64.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Adam Rudd. 3 | * MIT License 4 | */ 5 | 6 | #include "Base64.h" 7 | 8 | const char b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 9 | "abcdefghijklmnopqrstuvwxyz" 10 | "0123456789+/"; 11 | 12 | /* 'Private' declarations */ 13 | inline void a3_to_a4(unsigned char *a4, unsigned char *a3); 14 | 15 | inline void a4_to_a3(unsigned char *a3, unsigned char *a4); 16 | 17 | inline unsigned char b64_lookup(char c); 18 | 19 | int base64_encode(char *output, char *input, int inputLen) { 20 | int i = 0, j = 0; 21 | int encLen = 0; 22 | unsigned char a3[3]; 23 | unsigned char a4[4]; 24 | 25 | while (inputLen--) { 26 | a3[i++] = *(input++); 27 | if (i == 3) { 28 | a3_to_a4(a4, a3); 29 | 30 | for (i = 0; i < 4; i++) { 31 | output[encLen++] = b64_alphabet[a4[i]]; 32 | } 33 | 34 | i = 0; 35 | } 36 | } 37 | 38 | if (i) { 39 | for (j = i; j < 3; j++) { 40 | a3[j] = '\0'; 41 | } 42 | 43 | a3_to_a4(a4, a3); 44 | 45 | for (j = 0; j < i + 1; j++) { 46 | output[encLen++] = b64_alphabet[a4[j]]; 47 | } 48 | 49 | while ((i++ < 3)) { 50 | output[encLen++] = '='; 51 | } 52 | } 53 | output[encLen] = '\0'; 54 | return encLen; 55 | } 56 | 57 | int base64_decode(char *output, char *input, int inputLen) { 58 | int i = 0, j = 0; 59 | int decLen = 0; 60 | unsigned char a3[3]; 61 | unsigned char a4[4]; 62 | 63 | 64 | while (inputLen--) { 65 | if (*input == '=') { 66 | break; 67 | } 68 | 69 | a4[i++] = *(input++); 70 | if (i == 4) { 71 | for (i = 0; i < 4; i++) { 72 | a4[i] = b64_lookup(a4[i]); 73 | } 74 | 75 | a4_to_a3(a3, a4); 76 | 77 | for (i = 0; i < 3; i++) { 78 | output[decLen++] = a3[i]; 79 | } 80 | i = 0; 81 | } 82 | } 83 | 84 | if (i) { 85 | for (j = i; j < 4; j++) { 86 | a4[j] = '\0'; 87 | } 88 | 89 | for (j = 0; j < 4; j++) { 90 | a4[j] = b64_lookup(a4[j]); 91 | } 92 | 93 | a4_to_a3(a3, a4); 94 | 95 | for (j = 0; j < i - 1; j++) { 96 | output[decLen++] = a3[j]; 97 | } 98 | } 99 | output[decLen] = '\0'; 100 | return decLen; 101 | } 102 | 103 | int base64_enc_len(int plainLen) { 104 | int n = plainLen; 105 | return (n + 2 - ((n + 2) % 3)) / 3 * 4; 106 | } 107 | 108 | int base64_dec_len(char *input, int inputLen) { 109 | int i = 0; 110 | int numEq = 0; 111 | for (i = inputLen - 1; input[i] == '='; i--) { 112 | numEq++; 113 | } 114 | 115 | return ((6 * inputLen) / 8) - numEq; 116 | } 117 | 118 | inline void a3_to_a4(unsigned char *a4, unsigned char *a3) { 119 | a4[0] = (a3[0] & 0xfc) >> 2; 120 | a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); 121 | a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); 122 | a4[3] = (a3[2] & 0x3f); 123 | } 124 | 125 | inline void a4_to_a3(unsigned char *a3, unsigned char *a4) { 126 | a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); 127 | a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); 128 | a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; 129 | } 130 | 131 | inline unsigned char b64_lookup(char c) { 132 | if (c >= 'A' && c <= 'Z') return c - 'A'; 133 | if (c >= 'a' && c <= 'z') return c - 71; 134 | if (c >= '0' && c <= '9') return c + 4; 135 | if (c == '+') return 62; 136 | if (c == '/') return 63; 137 | return -1; 138 | } 139 | -------------------------------------------------------------------------------- /hackeeg_driver/Base64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Adam Rudd. 3 | * MIT License 4 | */ 5 | 6 | #ifndef _BASE64_H 7 | #define _BASE64_H 8 | 9 | /* b64_alphabet: 10 | * Description: Base64 alphabet table, a mapping between integers 11 | * and base64 digits 12 | * Notes: This is an extern here but is defined in Base64.c 13 | */ 14 | extern const char b64_alphabet[]; 15 | 16 | /* base64_encode: 17 | * Description: 18 | * Encode a string of characters as base64 19 | * Parameters: 20 | * output: the output buffer for the encoding, stores the encoded string 21 | * input: the input buffer for the encoding, stores the binary to be encoded 22 | * inputLen: the length of the input buffer, in bytes 23 | * Return value: 24 | * Returns the length of the encoded string 25 | * Requirements: 26 | * 1. output must not be null or empty 27 | * 2. input must not be null 28 | * 3. inputLen must be greater than or equal to 0 29 | */ 30 | int base64_encode(char *output, char *input, int inputLen); 31 | 32 | /* base64_decode: 33 | * Description: 34 | * Decode a base64 encoded string into bytes 35 | * Parameters: 36 | * output: the output buffer for the decoding, 37 | * stores the decoded binary 38 | * input: the input buffer for the decoding, 39 | * stores the base64 string to be decoded 40 | * inputLen: the length of the input buffer, in bytes 41 | * Return value: 42 | * Returns the length of the decoded string 43 | * Requirements: 44 | * 1. output must not be null or empty 45 | * 2. input must not be null 46 | * 3. inputLen must be greater than or equal to 0 47 | */ 48 | int base64_decode(char *output, char *input, int inputLen); 49 | 50 | /* base64_enc_len: 51 | * Description: 52 | * Returns the length of a base64 encoded string whose decoded 53 | * form is inputLen bytes long 54 | * Parameters: 55 | * inputLen: the length of the decoded string 56 | * Return value: 57 | * The length of a base64 encoded string whose decoded form 58 | * is inputLen bytes long 59 | * Requirements: 60 | * None 61 | */ 62 | int base64_enc_len(int inputLen); 63 | 64 | /* base64_dec_len: 65 | * Description: 66 | * Returns the length of the decoded form of a 67 | * base64 encoded string 68 | * Parameters: 69 | * input: the base64 encoded string to be measured 70 | * inputLen: the length of the base64 encoded string 71 | * Return value: 72 | * Returns the length of the decoded form of a 73 | * base64 encoded string 74 | * Requirements: 75 | * 1. input must not be null 76 | * 2. input must be greater than or equal to zero 77 | */ 78 | int base64_dec_len(char *input, int inputLen); 79 | 80 | #endif // _BASE64_H 81 | -------------------------------------------------------------------------------- /hackeeg_driver/JsonCommand.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * JsonCommand - A Wiring/Arduino library that uses JsonLines as 3 | * a protocol for sending commands and receiving data 4 | * over a serial port. 5 | * 6 | * Copyright (C) 2013-2019 Adam Feuer 7 | * 8 | * This library is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU Lesser General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this library. If not, see . 20 | */ 21 | 22 | // uncomment for debugging on Serial interface (programming port) 23 | // you must connect to Serial port first, then SerialUSB, since Serial will reset the Arduino Due 24 | //#define JSONCOMMAND_DEBUG 1 25 | 26 | #include "JsonCommand.h" 27 | 28 | const char *COMMAND_KEY = "COMMAND"; 29 | const char *PARAMETERS_KEY = "PARAMETERS"; 30 | const char *STATUS_CODE_KEY = "STATUS_CODE"; 31 | const char *STATUS_TEXT_KEY = "STATUS_TEXT"; 32 | const char *HEADERS_KEY = "HEADERS"; 33 | const char *DATA_KEY = "DATA"; 34 | 35 | const char *MP_COMMAND_KEY = "C"; 36 | const char *MP_PARAMETERS_KEY = "P"; 37 | const char *MP_STATUS_CODE_KEY = "C"; 38 | const char *MP_STATUS_TEXT_KEY = "T"; 39 | const char *MP_HEADERS_KEY = "H"; 40 | const char *MP_DATA_KEY = "D"; 41 | /** 42 | * Constructor makes sure some things are set. 43 | */ 44 | JsonCommand::JsonCommand() 45 | : commandList(NULL), 46 | commandCount(0), 47 | defaultHandler(NULL), 48 | term('\n'), // default terminator for commands, newline character 49 | last(NULL) { 50 | 51 | strcpy(delim, " "); // strtok_r needs a null-terminated string 52 | clearBuffer(); 53 | #ifdef JSONCOMMAND_DEBUG 54 | Serial.begin(BAUD_RATE); // for debugging 55 | #endif 56 | } 57 | 58 | /** 59 | * Adds a "command" and a handler function to the list of available commands. 60 | * This is used for matching a found token in the buffer, and gives the pointer 61 | * to the handler function to deal with it. 62 | */ 63 | void JsonCommand::addCommand(const char *command, 64 | void (*func)(unsigned char register_number, unsigned char register_value)) { 65 | #ifdef JSONCOMMAND_DEBUG 66 | Serial.print("Adding command ("); 67 | Serial.print(commandCount); 68 | Serial.print("): "); 69 | Serial.println(command); 70 | #endif 71 | 72 | commandList = (JsonCommandCallback *) realloc(commandList, (commandCount + 1) * sizeof(JsonCommandCallback)); 73 | strncpy(commandList[commandCount].command, command, JSONCOMMAND_MAXCOMMANDLENGTH); 74 | commandList[commandCount].command_function = func; 75 | commandCount++; 76 | 77 | #ifdef JSONCOMMAND_DEBUG 78 | Serial.begin(BAUD_RATE); // for debugging 79 | Serial.println("Debug serial logging ready."); 80 | #endif 81 | } 82 | 83 | /** 84 | * This sets up a handler to be called in the event that the receveived command string 85 | * isn't in the list of commands. 86 | */ 87 | void JsonCommand::setDefaultHandler(void (*func)(const char *)) { 88 | defaultHandler = func; 89 | } 90 | 91 | 92 | /** 93 | * This checks the Serial stream for characters, and assembles them into a buffer. 94 | * When the terminator character (default '\n') is seen, it starts parsing the 95 | * buffer as a JSON document (JSONLines). 96 | * 97 | * Then it checks to see if it has the proper structure as a command. If so, 98 | * it runs the command and outputs the result as a JSONLines document to the 99 | * Serial stream. 100 | */ 101 | void JsonCommand::readSerial() { 102 | StaticJsonDocument<1024> json_command; 103 | while (SerialUSB.available() > 0) { 104 | char inChar = SerialUSB.read(); // Read single available character, there may be more waiting 105 | #ifdef JSONCOMMAND_DEBUG 106 | Serial.print(inChar); // Echo back to serial stream 107 | #endif 108 | 109 | if (inChar == term) { // Check for the terminator (default '\r') meaning end of command 110 | #ifdef JSONCOMMAND_DEBUG 111 | Serial.println(); 112 | Serial.print("Received: "); 113 | Serial.println(buffer); 114 | #endif 115 | DeserializationError error = deserializeJson(json_command, buffer); 116 | 117 | if (error) { 118 | #ifdef JSONCOMMAND_DEBUG 119 | Serial.print(F("deserializeJson() failed: ")); 120 | Serial.println(error.c_str()); 121 | #endif 122 | clearBuffer(); 123 | sendJsonLinesResponse(400, "Bad Request"); 124 | return; 125 | } 126 | 127 | JsonObject command_object = json_command.as(); 128 | JsonVariant command_name_variant = command_object.getMember(COMMAND_KEY); 129 | if (command_name_variant.isNull()) { 130 | #ifdef JSONCOMMAND_DEBUG 131 | Serial.println(F("Error: no command")); 132 | #endif 133 | (*defaultHandler)(""); 134 | clearBuffer(); 135 | return; 136 | } 137 | const char *command = command_name_variant.as(); 138 | int command_num = findCommand(command); 139 | if (command_num < 0) { 140 | (*defaultHandler)(command); 141 | clearBuffer(); 142 | return; 143 | } 144 | #ifdef JSONCOMMAND_DEBUG 145 | Serial.println(commandList[command_num].command); 146 | #endif 147 | JsonVariant parameters_variant = json_command.getMember(PARAMETERS_KEY); 148 | unsigned char register_number = 0; 149 | unsigned char register_value = 0; 150 | if (!parameters_variant.isNull()) { 151 | JsonArray params_array = parameters_variant.as(); 152 | size_t number_of_params = params_array.size(); 153 | #ifdef JSONCOMMAND_DEBUG 154 | Serial.println(number_of_params); 155 | #endif 156 | if (number_of_params > 0) { 157 | register_number = params_array[0]; 158 | } 159 | if (number_of_params > 1) { 160 | register_value = params_array[1]; 161 | } 162 | } 163 | // Execute the stored handler function for the command 164 | (*commandList[command_num].command_function)(register_number, register_value); 165 | clearBuffer(); 166 | } else { 167 | if (bufPos < JSONCOMMAND_BUFFER) { 168 | buffer[bufPos++] = inChar; // Put character into buffer 169 | buffer[bufPos] = '\0'; // Null terminate 170 | } else { 171 | #ifdef JSONCOMMAND_DEBUG 172 | Serial.println("Line buffer is full - increase JSONCOMMAND_BUFFER"); 173 | #endif 174 | } 175 | } 176 | } 177 | 178 | } 179 | 180 | 181 | int JsonCommand::findCommand(const char *command) { 182 | int result = -1; 183 | for (int i = 0; i < commandCount; i++) { 184 | if (strcmp(command, commandList[i].command) == 0) { 185 | result = i; 186 | break; 187 | } 188 | } 189 | return result; 190 | } 191 | 192 | void JsonCommand::sendJsonLinesResponse(int status_code, char *status_text) { 193 | StaticJsonDocument<1024> doc; 194 | JsonObject root = doc.to(); 195 | root[STATUS_CODE_KEY] = status_code; 196 | root[STATUS_TEXT_KEY] = status_text; 197 | serializeJson(doc, SerialUSB); 198 | SerialUSB.println(); 199 | doc.clear(); 200 | } 201 | 202 | void JsonCommand::sendJsonLinesDocResponse(JsonDocument &doc) { 203 | serializeJson(doc, SerialUSB); 204 | SerialUSB.println(); 205 | doc.clear(); 206 | } 207 | 208 | void JsonCommand::sendMessagePackResponse(int status_code, char *status_text) { 209 | StaticJsonDocument<1024> doc; 210 | JsonObject root = doc.to(); 211 | root[MP_STATUS_CODE_KEY] = status_code; 212 | if (!status_text) { 213 | root[MP_STATUS_TEXT_KEY] = status_text; 214 | } 215 | serializeMsgPack(doc, SerialUSB); 216 | doc.clear(); 217 | } 218 | 219 | void JsonCommand::sendMessagePackDocResponse(JsonDocument &doc) { 220 | serializeMsgPack(doc, SerialUSB); 221 | doc.clear(); 222 | } 223 | 224 | /** 225 | * Clear the input buffer. 226 | */ 227 | void JsonCommand::clearBuffer() { 228 | buffer[0] = '\0'; 229 | bufPos = 0; 230 | } 231 | 232 | /** 233 | * Retrieve the next token ("word" or "argument") from the command buffer. 234 | * Returns NULL if no more tokens exist. 235 | */ 236 | char *JsonCommand::next() { 237 | return strtok_r(NULL, delim, &last); 238 | } 239 | 240 | -------------------------------------------------------------------------------- /hackeeg_driver/JsonCommand.h: -------------------------------------------------------------------------------- 1 | /** 2 | * JsonCommand - A Wiring/Arduino library that uses JsonLines as 3 | * a protocol for sending commands and receiving data 4 | * over a serial port. 5 | * 6 | * Copyright (C) 2013-2019 Adam Feuer 7 | * 8 | * This library is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU Lesser General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this library. If not, see . 20 | */ 21 | #ifndef JSONCOMMAND_H 22 | #define JSONCOMMAND_H 23 | 24 | #if defined(WIRING) && WIRING >= 100 25 | #include 26 | #elif defined(ARDUINO) && ARDUINO >= 100 27 | #include 28 | #else 29 | 30 | #include 31 | 32 | #endif 33 | 34 | #include 35 | #include 36 | 37 | // Size of the input buffer in bytes (maximum length of one command plus arguments) 38 | #define JSONCOMMAND_BUFFER 1024 39 | // Maximum length of a command excluding the terminating null 40 | #define JSONCOMMAND_MAXCOMMANDLENGTH 128 41 | 42 | #define BAUD_RATE 115200 // WiredSerial ignores this and uses the maximum rate 43 | 44 | // Uncomment the next line to run the library in debug mode (verbose messages) 45 | //#define JSONCOMMAND_DEBUG 46 | 47 | #define STATUS_OK 200 48 | 49 | extern const char *COMMAND_KEY; 50 | extern const char *PARAMETERS_KEY; 51 | extern const char *STATUS_CODE_KEY; 52 | extern const char *STATUS_TEXT_KEY; 53 | extern const char *HEADERS_KEY; 54 | extern const char *DATA_KEY; 55 | 56 | extern const char *MP_COMMAND_KEY; 57 | extern const char *MP_PARAMETERS_KEY; 58 | extern const char *MP_STATUS_CODE_KEY; 59 | extern const char *MP_STATUS_TEXT_KEY; 60 | extern const char *MP_HEADERS_KEY; 61 | extern const char *MP_DATA_KEY; 62 | 63 | typedef void (*command_func)(unsigned char, unsigned char); 64 | 65 | class JsonCommand { 66 | public: 67 | JsonCommand(); // Constructor 68 | void addCommand(const char *command, void (*func)(unsigned char register_number, 69 | unsigned char register_value)); // Add a command to the processing dictionary. 70 | void setDefaultHandler(void (*function)(const char *)); // A handler to call when no valid command received. 71 | 72 | void readSerial(); // Main entry point. 73 | void readSerialMessagePackMessage(); // Entry point for MessagePack mode 74 | void clearBuffer(); // Clears the input buffer. 75 | void printCommands(); // Prints the list of commands. 76 | char * next(); // Returns pointer to next token found in command buffer (for getting arguments to commands). 77 | void sendJsonLinesResponse(int status_code, char *status_text); // send a simple JSON Lines response 78 | void sendJsonLinesDocResponse(JsonDocument &doc); // send a JsonDocument as a JSON Lines response 79 | void sendMessagePackResponse(int status_code, char *status_text); // send a simple MessagePack response 80 | void sendMessagePackDocResponse(JsonDocument &doc); // send a JsonDocument as a MessagePack response 81 | 82 | private: 83 | // Command/handler dictionary 84 | struct JsonCommandCallback { 85 | char command[JSONCOMMAND_MAXCOMMANDLENGTH + 1]; 86 | command_func command_function; 87 | }; // Data structure to hold Command/Handler function key-value pairs 88 | JsonCommandCallback *commandList; // Actual definition for command/handler array 89 | byte commandCount; 90 | 91 | // Pointer to the default handler function 92 | void (*defaultHandler)(const char *); 93 | 94 | char delim[2]; // null-terminated list of character to be used as delimeters for tokenizing (default " ") 95 | char term; // Character that signals end of command (default '\n') 96 | 97 | char buffer[JSONCOMMAND_BUFFER + 1]; // Buffer of stored characters while waiting for terminator character 98 | byte bufPos; // Current position in the buffer 99 | char *last; // State variable used by strtok_r during processing 100 | 101 | int findCommand(const char *command); 102 | }; 103 | 104 | #endif // JSONCOMMAND_H 105 | -------------------------------------------------------------------------------- /hackeeg_driver/SerialCommand.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * SerialCommand - A Wiring/Arduino library to tokenize and parse commands 3 | * received over a serial port. 4 | * 5 | * Copyright (C) 2013-2019 Adam Feuer 6 | * 7 | * This library is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Lesser General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this library. If not, see . 19 | */ 20 | #include "SerialCommand.h" 21 | 22 | /** 23 | * Constructor makes sure some things are set. 24 | */ 25 | SerialCommand::SerialCommand() 26 | : commandList(NULL), 27 | commandCount(0), 28 | defaultHandler(NULL), 29 | term('\n'), // default terminator for commands, newline character 30 | last(NULL) { 31 | strcpy(delim, " "); // strtok_r needs a null-terminated string 32 | clearBuffer(); 33 | } 34 | 35 | /** 36 | * Adds a "command" and a handler function to the list of available commands. 37 | * This is used for matching a found token in the buffer, and gives the pointer 38 | * to the handler function to deal with it. 39 | */ 40 | void SerialCommand::addCommand(const char *command, 41 | void (*func)(unsigned char register_number, unsigned char register_value)) { 42 | #ifdef SERIALCOMMAND_DEBUG 43 | SerialUSB.print("Adding command ("); 44 | SerialUSB.print(commandCount); 45 | SerialUSB.print("): "); 46 | SerialUSB.println(command); 47 | #endif 48 | 49 | commandList = (SerialCommandCallback *) realloc(commandList, (commandCount + 1) * sizeof(SerialCommandCallback)); 50 | strncpy(commandList[commandCount].command, command, SERIALCOMMAND_MAXCOMMANDLENGTH); 51 | commandList[commandCount].command_function = func; 52 | commandCount++; 53 | } 54 | 55 | /** 56 | * This sets up a handler to be called in the event that the receveived command string 57 | * isn't in the list of commands. 58 | */ 59 | void SerialCommand::setDefaultHandler(void (*function)(const char *)) { 60 | defaultHandler = function; 61 | } 62 | 63 | 64 | /** 65 | * This checks the Serial stream for characters, and assembles them into a buffer. 66 | * When the terminator character (default '\n') is seen, it starts parsing the 67 | * buffer for a prefix command, and calls handlers setup by addCommand() member 68 | */ 69 | void SerialCommand::readSerial() { 70 | while (SerialUSB.available() > 0) { 71 | char inChar = SerialUSB.read(); // Read single available character, there may be more waiting 72 | #ifdef SERIALCOMMAND_DEBUG 73 | SerialUSB.print(inChar); // Echo back to serial stream 74 | #endif 75 | 76 | inChar = tolower(inChar); 77 | if (inChar == term) { // Check for the terminator (default '\r') meaning end of command 78 | #ifdef SERIALCOMMAND_DEBUG 79 | SerialUSB.print("Received: "); 80 | SerialUSB.println(buffer); 81 | #endif 82 | 83 | char *command = strtok_r(buffer, delim, &last); // Search for command at start of buffer 84 | if (command != NULL) { 85 | boolean matched = false; 86 | 87 | for (int i = 0; i < commandCount; i++) { 88 | #ifdef SERIALCOMMAND_DEBUG 89 | SerialUSB.print("Comparing ["); 90 | SerialUSB.print(command); 91 | SerialUSB.print("] to ["); 92 | SerialUSB.print(commandList[i].command); 93 | SerialUSB.println("]"); 94 | #endif 95 | 96 | // Compare the found command against the list of known commands for a match 97 | if (strncmp(command, commandList[i].command, SERIALCOMMAND_MAXCOMMANDLENGTH) == 0) { 98 | #ifdef SERIALCOMMAND_DEBUG 99 | SerialUSB.print("Matched Command: "); 100 | SerialUSB.println(command); 101 | #endif 102 | 103 | // Execute the stored handler function for the command 104 | unsigned char unused1 = 0; 105 | unsigned char unused2 = 0; 106 | (*commandList[i].command_function)(unused1, unused2); 107 | matched = true; 108 | break; 109 | } 110 | } 111 | if (!matched && (defaultHandler != NULL)) { 112 | (*defaultHandler)(command); 113 | } 114 | } 115 | clearBuffer(); 116 | } else if (isprint(inChar)) { // Only printable characters into the buffer 117 | if (bufPos < SERIALCOMMAND_BUFFER) { 118 | buffer[bufPos++] = inChar; // Put character into buffer 119 | buffer[bufPos] = '\0'; // Null terminate 120 | } else { 121 | #ifdef SERIALCOMMAND_DEBUG 122 | Serial.println("Line buffer is full - increase SERIALCOMMAND_BUFFER"); 123 | #endif 124 | } 125 | } 126 | } 127 | } 128 | 129 | 130 | /** 131 | * Clear the input buffer. 132 | */ 133 | void SerialCommand::clearBuffer() { 134 | buffer[0] = '\0'; 135 | bufPos = 0; 136 | } 137 | 138 | /** 139 | * Retrieve the next token ("word" or "argument") from the command buffer. 140 | * Returns NULL if no more tokens exist. 141 | */ 142 | char *SerialCommand::next() { 143 | return strtok_r(NULL, delim, &last); 144 | } 145 | 146 | /** 147 | * Print the list of commands. 148 | */ 149 | 150 | void SerialCommand::printCommands() { 151 | for (int i = 0; i < commandCount; i++) { 152 | SerialUSB.println(commandList[i].command); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /hackeeg_driver/SerialCommand.h: -------------------------------------------------------------------------------- 1 | /** 2 | * SerialCommand - A Wiring/Arduino library to tokenize and parse commands 3 | * received over a serial port. 4 | * 5 | * Copyright (C) 2013-2019 Adam Feuer 6 | * 7 | * This library is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Lesser General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this library. If not, see . 19 | */ 20 | #ifndef SERIALCOMMAND_H 21 | #define SERIALCOMMAND_H 22 | 23 | // uncomment for debugging on Serial interface (programming port) 24 | // you must connect to Serial port first, then SerialUSB, since Serial will reset the Arduino Due 25 | //#define SERIALCOMMAND_DEBUG 1 26 | 27 | #if defined(WIRING) && WIRING >= 100 28 | #include 29 | #elif defined(ARDUINO) && ARDUINO >= 100 30 | #include 31 | #else 32 | 33 | #include 34 | 35 | #endif 36 | 37 | #include 38 | 39 | // Size of the input buffer in bytes (maximum length of one command plus arguments) 40 | #define SERIALCOMMAND_BUFFER 128 41 | // Maximum length of a command excluding the terminating null 42 | #define SERIALCOMMAND_MAXCOMMANDLENGTH 32 43 | 44 | typedef void (*command_func)(unsigned char, unsigned char); 45 | 46 | class SerialCommand { 47 | public: 48 | SerialCommand(); // Constructor 49 | void addCommand(const char *command, void (*func)(unsigned char register_number, 50 | unsigned char register_value)); // Add a command to the processing dictionary. 51 | void setDefaultHandler(void (*function)(const char *)); // A handler to call when no valid command received. 52 | 53 | void readSerial(); // Main entry point. 54 | void clearBuffer(); // Clears the input buffer. 55 | void printCommands(); // Prints the list of commands. 56 | char * next(); // Returns pointer to next token found in command buffer 57 | // (for getting arguments to commands). 58 | 59 | private: 60 | // Command/handler dictionary 61 | struct SerialCommandCallback { 62 | char command[SERIALCOMMAND_MAXCOMMANDLENGTH + 1]; 63 | command_func command_function; 64 | }; // Data structure to hold Command/Handler function key-value pairs 65 | SerialCommandCallback *commandList; // Actual definition for command/handler array 66 | byte commandCount; 67 | 68 | // Pointer to the default handler function 69 | void (*defaultHandler)(const char *); 70 | 71 | char delim[2]; // null-terminated list of character to be used as delimeters for tokenizing (default " ") 72 | char term; // Character that signals end of command (default '\n') 73 | 74 | char buffer[SERIALCOMMAND_BUFFER + 1]; // Buffer of stored characters while waiting for terminator character 75 | byte bufPos; // Current position in the buffer 76 | char *last; // State variable used by strtok_r during processing 77 | }; 78 | 79 | #endif // SERIALCOMMAND_H 80 | -------------------------------------------------------------------------------- /hackeeg_driver/SpiDma.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2019 by Adam Feuer 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this library. If not, see . 16 | * 17 | */ 18 | 19 | #include 20 | #include 21 | #include "Arduino.h" 22 | 23 | #define USE_ARDUINO_SPI_LIBRARY 1 24 | #define USE_NATIVE_SAM3X_SPI 0 25 | 26 | 27 | #if USE_ARDUINO_SPI_LIBRARY 28 | 29 | #include 30 | 31 | void spiBegin(uint8_t csPin) { 32 | SPI.begin(); 33 | pinMode(csPin, OUTPUT); 34 | } 35 | 36 | void spiInit(uint8_t bitOrder, uint8_t spiMode, uint8_t spiClockDivider) { 37 | SPI.setBitOrder((BitOrder) bitOrder); // MSBFIRST or LSBFIRST 38 | SPI.setDataMode(spiMode); // SPI_MODE0, SPI_MODE1; SPI_MODE2; SPI_MODE3 39 | SPI.setClockDivider(spiClockDivider); 40 | } 41 | 42 | /** SPI receive a byte */ 43 | uint8_t spiRec() { 44 | noInterrupts(); 45 | return SPI.transfer(0x00); 46 | interrupts(); 47 | } 48 | 49 | /** SPI receive multiple bytes */ 50 | uint8_t spiRec(uint8_t *buf, size_t len) { 51 | memset(buf, 0, len); 52 | noInterrupts(); 53 | SPI.transfer((void *)buf, len); 54 | interrupts(); 55 | return 0; 56 | } 57 | 58 | /** SPI send a byte */ 59 | void spiSend(uint8_t b) { 60 | noInterrupts(); 61 | SPI.transfer(b); 62 | interrupts(); 63 | } 64 | 65 | /** SPI send multiple bytes */ 66 | void spiSend(const uint8_t *buf, size_t len) { 67 | noInterrupts(); 68 | SPI.transfer((void *)buf, len); 69 | interrupts(); 70 | } 71 | 72 | #elif USE_NATIVE_SAM3X_SPI 73 | #include 74 | #include "variant.h" 75 | /** Use SAM3X DMAC if nonzero */ 76 | #define USE_SAM3X_DMAC 1 77 | /** Use extra Bus Matrix arbitration fix if nonzero */ 78 | #define USE_SAM3X_BUS_MATRIX_FIX 0 79 | /** Time in ms for DMA receive timeout */ 80 | #define SAM3X_DMA_TIMEOUT 100 81 | /** chip select register number */ 82 | #define SPI_CHIP_SEL 3 83 | /** DMAC receive channel */ 84 | #define SPI_DMAC_RX_CH 1 85 | /** DMAC transmit channel */ 86 | #define SPI_DMAC_TX_CH 0 87 | /** DMAC Channel HW Interface Number for SPI TX. */ 88 | #define SPI_TX_IDX 1 89 | /** DMAC Channel HW Interface Number for SPI RX. */ 90 | #define SPI_RX_IDX 2 91 | 92 | uint8_t bitOrder = MSBFIRST; 93 | 94 | /** Disable DMA Controller. */ 95 | static void dmac_disable() { 96 | DMAC->DMAC_EN &= (~DMAC_EN_ENABLE); 97 | } 98 | /** Enable DMA Controller. */ 99 | static void dmac_enable() { 100 | DMAC->DMAC_EN = DMAC_EN_ENABLE; 101 | } 102 | /** Disable DMA Channel. */ 103 | static void dmac_channel_disable(uint32_t ul_num) { 104 | DMAC->DMAC_CHDR = DMAC_CHDR_DIS0 << ul_num; 105 | } 106 | /** Enable DMA Channel. */ 107 | static void dmac_channel_enable(uint32_t ul_num) { 108 | DMAC->DMAC_CHER = DMAC_CHER_ENA0 << ul_num; 109 | } 110 | /** Poll for transfer complete. */ 111 | static bool dmac_channel_transfer_done(uint32_t ul_num) { 112 | return (DMAC->DMAC_CHSR & (DMAC_CHSR_ENA0 << ul_num)) ? false : true; 113 | } 114 | 115 | void spiBegin(uint8_t csPin) { 116 | pinMode(csPin,OUTPUT); 117 | digitalWrite(csPin,HIGH); 118 | PIO_Configure( 119 | g_APinDescription[PIN_SPI_MOSI].pPort, 120 | g_APinDescription[PIN_SPI_MOSI].ulPinType, 121 | g_APinDescription[PIN_SPI_MOSI].ulPin, 122 | g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration); 123 | PIO_Configure( 124 | g_APinDescription[PIN_SPI_MISO].pPort, 125 | g_APinDescription[PIN_SPI_MISO].ulPinType, 126 | g_APinDescription[PIN_SPI_MISO].ulPin, 127 | g_APinDescription[PIN_SPI_MISO].ulPinConfiguration); 128 | PIO_Configure( 129 | g_APinDescription[PIN_SPI_SCK].pPort, 130 | g_APinDescription[PIN_SPI_SCK].ulPinType, 131 | g_APinDescription[PIN_SPI_SCK].ulPin, 132 | g_APinDescription[PIN_SPI_SCK].ulPinConfiguration); 133 | pmc_enable_periph_clk(ID_SPI0); 134 | #if USE_SAM3X_DMAC 135 | pmc_enable_periph_clk(ID_DMAC); 136 | dmac_disable(); 137 | DMAC->DMAC_GCFG = DMAC_GCFG_ARB_CFG_FIXED; 138 | dmac_enable(); 139 | #if USE_SAM3X_BUS_MATRIX_FIX 140 | MATRIX->MATRIX_WPMR = 0x4d415400; 141 | MATRIX->MATRIX_MCFG[1] = 1; 142 | MATRIX->MATRIX_MCFG[2] = 1; 143 | MATRIX->MATRIX_SCFG[0] = 0x01000010; 144 | MATRIX->MATRIX_SCFG[1] = 0x01000010; 145 | MATRIX->MATRIX_SCFG[7] = 0x01000010; 146 | #endif // USE_SAM3X_BUS_MATRIX_FIX 147 | #endif // USE_SAM3X_DMAC 148 | } 149 | 150 | // start RX DMA 151 | void spiDmaRX(uint8_t* dst, uint16_t count) { 152 | dmac_channel_disable(SPI_DMAC_RX_CH); 153 | DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_SADDR = (uint32_t)&SPI0->SPI_RDR; 154 | DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DADDR = (uint32_t)dst; 155 | DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DSCR = 0; 156 | DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLA = count | 157 | DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE; 158 | DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR | 159 | DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_PER2MEM_DMA_FC | 160 | DMAC_CTRLB_SRC_INCR_FIXED | DMAC_CTRLB_DST_INCR_INCREMENTING; 161 | DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CFG = DMAC_CFG_SRC_PER(SPI_RX_IDX) | 162 | DMAC_CFG_SRC_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ASAP_CFG; 163 | dmac_channel_enable(SPI_DMAC_RX_CH); 164 | } 165 | 166 | // start TX DMA 167 | void spiDmaTX(const uint8_t* src, uint16_t count) { 168 | // static uint8_t ff = 0XFF; 169 | static uint8_t dummy_data = 0x00; 170 | uint32_t src_incr = DMAC_CTRLB_SRC_INCR_INCREMENTING; 171 | if (!src) { 172 | src = &dummy_data; 173 | // src = &ff; 174 | src_incr = DMAC_CTRLB_SRC_INCR_FIXED; 175 | } 176 | dmac_channel_disable(SPI_DMAC_TX_CH); 177 | DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_SADDR = (uint32_t)src; 178 | DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DADDR = (uint32_t)&SPI0->SPI_TDR; 179 | DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DSCR = 0; 180 | DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLA = count | 181 | DMAC_CTRLA_SRC_WIDTH_BYTE | DMAC_CTRLA_DST_WIDTH_BYTE; 182 | 183 | DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLB = DMAC_CTRLB_SRC_DSCR | 184 | DMAC_CTRLB_DST_DSCR | DMAC_CTRLB_FC_MEM2PER_DMA_FC | 185 | src_incr | DMAC_CTRLB_DST_INCR_FIXED; 186 | 187 | DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CFG = DMAC_CFG_DST_PER(SPI_TX_IDX) | 188 | DMAC_CFG_DST_H2SEL | DMAC_CFG_SOD | DMAC_CFG_FIFOCFG_ALAP_CFG; 189 | 190 | dmac_channel_enable(SPI_DMAC_TX_CH); 191 | } 192 | 193 | // initialize SPI controller 194 | void spiInit(uint8_t bitOrder, uint8_t spiMode, uint8_t spiClockDivider) { 195 | uint8_t scbr; 196 | Spi* pSpi = SPI0; 197 | scbr = spiClockDivider; 198 | // disable SPI 199 | pSpi->SPI_CR = SPI_CR_SPIDIS; 200 | // reset SPI 201 | pSpi->SPI_CR = SPI_CR_SWRST; 202 | // no mode fault detection, set master mode 203 | pSpi->SPI_MR = SPI_PCS(SPI_CHIP_SEL) | SPI_MR_MODFDIS | SPI_MR_MSTR; 204 | if (spiMode == SPI_MODE0) { // SPI_MODE0, SPI_MODE1; other modes currently not supported. 205 | // mode 0, 8-bit, 206 | pSpi->SPI_CSR[SPI_CHIP_SEL] = SPI_CSR_SCBR(scbr) | SPI_CSR_NCPHA; 207 | } else { 208 | // mode 1, 8-bit, 209 | pSpi->SPI_CSR[SPI_CHIP_SEL] = SPI_CSR_SCBR(scbr); 210 | } 211 | // enable SPI 212 | pSpi->SPI_CR |= SPI_CR_SPIEN; 213 | } 214 | 215 | inline uint8_t spiTransfer(uint8_t b) { 216 | Spi* pSpi = SPI0; 217 | 218 | pSpi->SPI_TDR = b; 219 | while ((pSpi->SPI_SR & SPI_SR_RDRF) == 0) {} 220 | b = pSpi->SPI_RDR; 221 | return b; 222 | } 223 | 224 | /** SPI receive a byte */ 225 | 226 | uint8_t spiRec() { 227 | // return spiTransfer(0XFF); 228 | return spiTransfer(0x00); 229 | } 230 | 231 | /** SPI receive multiple bytes */ 232 | uint8_t spiRec(uint8_t* buf, size_t len) { 233 | Spi* pSpi = SPI0; 234 | int rtn = 0; 235 | #if USE_SAM3X_DMAC 236 | // clear overrun error 237 | uint32_t s = pSpi->SPI_SR; 238 | 239 | spiDmaRX(buf, len); 240 | spiDmaTX(0, len); 241 | 242 | uint32_t m = millis(); 243 | while (!dmac_channel_transfer_done(SPI_DMAC_RX_CH)) { 244 | if ((millis() - m) > SAM3X_DMA_TIMEOUT) { 245 | dmac_channel_disable(SPI_DMAC_RX_CH); 246 | dmac_channel_disable(SPI_DMAC_TX_CH); 247 | rtn = 2; 248 | break; 249 | } 250 | } 251 | if (pSpi->SPI_SR & SPI_SR_OVRES) rtn |= 1; 252 | #else // USE_SAM3X_DMAC 253 | for (size_t i = 0; i < len; i++) { 254 | // pSpi->SPI_TDR = 0XFF; 255 | pSpi->SPI_TDR = 0x00; 256 | while ((pSpi->SPI_SR & SPI_SR_RDRF) == 0) {} 257 | buf[i] = pSpi->SPI_RDR; 258 | } 259 | #endif // USE_SAM3X_DMAC 260 | 261 | if (bitOrder == LSBFIRST) { 262 | for (register int i=0; iSPI_SR & SPI_SR_TXEMPTY) == 0) {} 282 | for (size_t i = 0; i < len; i++) { 283 | pSpi->SPI_TDR = buf[i]; 284 | while ((pSpi->SPI_SR & SPI_SR_TDRE) == 0) {} 285 | } 286 | #endif // #if USE_SAM3X_DMAC 287 | while ((pSpi->SPI_SR & SPI_SR_TXEMPTY) == 0) {} 288 | // leave RDR empty 289 | uint8_t b = pSpi->SPI_RDR; 290 | } 291 | #endif 292 | 293 | 294 | -------------------------------------------------------------------------------- /hackeeg_driver/SpiDma.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2019 by Adam Feuer 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this library. If not, see . 16 | * 17 | */ 18 | #ifndef SPI_DMA_H 19 | #define SPI_DMA_H 20 | 21 | // SPI clock divider - 1-255, divides 84Mhz system clock 22 | // 21 = 4 Mhz 23 | // 6 = 14 Mhz 24 | // 5 = 16.8 Mhz 25 | // 4 = 21 Mhz 26 | // ADS1299 needs the SPI clock to be 20Mhz or less 27 | 28 | #define SPI_CLOCK_DIVIDER 5 29 | 30 | void spiBegin(uint8_t csPin); 31 | 32 | void spiInit(uint8_t bitOrder, uint8_t spiMode, uint8_t spiClockDivider); 33 | 34 | uint8_t spiRec(); 35 | 36 | uint8_t spiRec(uint8_t *buf, size_t len); 37 | 38 | void spiSend(uint8_t b); 39 | 40 | void spiSend(const uint8_t *buf, size_t len); 41 | 42 | #endif // SPI_DMA_H 43 | -------------------------------------------------------------------------------- /hackeeg_driver/ads129x.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013-2019 by Adam Feuer 3 | Copyright (c) 2012-2016 by Kendrick Shaw, Ace Medlock, and Eric Herman 4 | 5 | This file is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Lesser General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (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 13 | GNU Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this library. If not, see . 17 | 18 | */ 19 | 20 | /* 21 | This file is mainly oriented on the ADS1299 and derivatives. 22 | ADS1298-only constants are prefixed with ADS1298_ 23 | 24 | */ 25 | 26 | #ifndef ADS129x_H 27 | #define ADS129x_H 28 | 29 | #ifdef __cplusplus 30 | namespace ADS129x { 31 | #endif 32 | 33 | enum spi_command { 34 | // system commands 35 | WAKEUP = 0x02, 36 | STANDBY = 0x04, 37 | RESET = 0x06, 38 | START = 0x08, 39 | STOP = 0x0a, 40 | 41 | // read commands 42 | RDATAC = 0x10, 43 | SDATAC = 0x11, 44 | RDATA = 0x12, 45 | 46 | // register commands 47 | RREG = 0x20, 48 | WREG = 0x40 49 | }; 50 | 51 | enum reg { 52 | // device settings 53 | ID = 0x00, 54 | 55 | // global settings 56 | CONFIG1 = 0x01, 57 | CONFIG2 = 0x02, 58 | CONFIG3 = 0x03, 59 | LOFF = 0x04, 60 | 61 | // channel specific settings 62 | CHnSET = 0x04, 63 | CH1SET = CHnSET + 1, 64 | CH2SET = CHnSET + 2, 65 | CH3SET = CHnSET + 3, 66 | CH4SET = CHnSET + 4, 67 | CH5SET = CHnSET + 5, 68 | CH6SET = CHnSET + 6, 69 | CH7SET = CHnSET + 7, 70 | CH8SET = CHnSET + 8, 71 | RLD_SENSP = 0x0d, 72 | RLD_SENSN = 0x0e, 73 | LOFF_SENSP = 0x0f, 74 | LOFF_SENSN = 0x10, 75 | LOFF_FLIP = 0x11, 76 | 77 | // lead off status 78 | LOFF_STATP = 0x12, 79 | LOFF_STATN = 0x13, 80 | 81 | // other 82 | GPIO = 0x14, 83 | PACE = 0x15, 84 | RESP = 0x16, 85 | CONFIG4 = 0x17, 86 | WCT1 = 0x18, 87 | WCT2 = 0x19 88 | }; 89 | 90 | enum ID_bits { 91 | DEV_ID7 = 0x80, 92 | DEV_ID6 = 0x40, 93 | DEV_ID5 = 0x20, 94 | DEV_ID2 = 0x04, 95 | DEV_ID1 = 0x02, 96 | DEV_ID0 = 0x01, 97 | 98 | ID_const = 0x10, 99 | ID_ADS129x = DEV_ID7, 100 | ID_ADS129xR = (DEV_ID7 | DEV_ID6), 101 | 102 | ID_4CHAN = 0, 103 | ID_6CHAN = DEV_ID0, 104 | ID_8CHAN = DEV_ID1, 105 | 106 | ID_ADS1294 = (ID_ADS129x | ID_4CHAN), 107 | ID_ADS1296 = (ID_ADS129x | ID_6CHAN), 108 | ID_ADS1298 = (ID_ADS129x | ID_8CHAN), 109 | ID_ADS1294R = (ID_ADS129xR | ID_4CHAN), 110 | ID_ADS1296R = (ID_ADS129xR | ID_6CHAN), 111 | ID_ADS1298R = (ID_ADS129xR | ID_8CHAN) 112 | }; 113 | 114 | enum CONFIG1_bits { 115 | HR = 0x80, 116 | DAISY_EN = 0x40, 117 | CLK_EN = 0x20, 118 | DR2 = 0x04, 119 | DR1 = 0x02, 120 | DR0 = 0x01, 121 | 122 | // correct for ADS1298/1294; incorrect for ADS1299 123 | // TODO: Fix for ADS1299 124 | 125 | // ADS1298 126 | //CONFIG1_const = 0x00, 127 | 128 | // ADS1299 129 | CONFIG1_const = 0xE0, 130 | 131 | // ADS1298 132 | /* 133 | HIGH_RES_32k_SPS = HR, 134 | HIGH_RES_16k_SPS = (HR | DR0), 135 | HIGH_RES_8k_SPS = (HR | DR1), 136 | HIGH_RES_4k_SPS = (HR | DR1 | DR0), 137 | HIGH_RES_2k_SPS = (HR | DR2), 138 | HIGH_RES_1k_SPS = (HR | DR2 | DR0), 139 | HIGH_RES_500_SPS = (HR | DR2 | DR1), 140 | LOW_POWR_250_SPS = (DR2 | DR1) 141 | */ 142 | 143 | // ADS1299 144 | HIGH_RES_16k_SPS = HR, 145 | HIGH_RES_8k_SPS = (HR | DR0), 146 | HIGH_RES_4k_SPS = (HR | DR1), 147 | HIGH_RES_2k_SPS = (HR | DR1 | DR0), 148 | HIGH_RES_1k_SPS = (HR | DR2), 149 | HIGH_RES_500_SPS = (HR | DR2 | DR0), 150 | HIGH_RES_250_SPS = (HR | DR2 | DR1), 151 | 152 | }; 153 | 154 | enum CONFIG2_bits { 155 | WCT_CHOP = 0x20, 156 | INT_TEST = 0x10, 157 | TEST_AMP = 0x04, 158 | TEST_FREQ1 = 0x02, 159 | TEST_FREQ0 = 0x01, 160 | 161 | // ADS1298 162 | //CONFIG2_const = 0x00, 163 | 164 | // ADS1299 165 | CONFIG2_const = 0xC0, 166 | 167 | INT_TEST_4HZ = INT_TEST, 168 | INT_TEST_8HZ = (INT_TEST | TEST_FREQ0), 169 | INT_TEST_DC = (INT_TEST | TEST_FREQ1 | TEST_FREQ0) 170 | }; 171 | 172 | enum CONFIG3_bits { 173 | PD_REFBUF = 0x80, 174 | VREF_4V = 0x20, 175 | RLD_MEAS = 0x10, 176 | RLDREF_INT = 0x08, 177 | PD_RLD = 0x04, 178 | RLD_LOFF_SENS = 0x02, 179 | RLD_STAT = 0x01, 180 | 181 | // ADS1298 182 | //CONFIG3_const = 0x40 183 | 184 | // ADS1299 185 | CONFIG3_const = 0x60 186 | }; 187 | 188 | enum LOFF_bits { 189 | COMP_TH2 = 0x80, 190 | COMP_TH1 = 0x40, 191 | COMP_TH0 = 0x20, 192 | VLEAD_OFF_EN = 0x10, 193 | ILEAD_OFF1 = 0x08, 194 | ILEAD_OFF0 = 0x04, 195 | FLEAD_OFF1 = 0x02, 196 | FLEAD_OFF0 = 0x01, 197 | 198 | LOFF_const = 0x00, 199 | 200 | COMP_TH_95 = 0x00, 201 | COMP_TH_92_5 = COMP_TH0, 202 | COMP_TH_90 = COMP_TH1, 203 | COMP_TH_87_5 = (COMP_TH1 | COMP_TH0), 204 | COMP_TH_85 = COMP_TH2, 205 | COMP_TH_80 = (COMP_TH2 | COMP_TH0), 206 | COMP_TH_75 = (COMP_TH2 | COMP_TH1), 207 | COMP_TH_70 = (COMP_TH2 | COMP_TH1 | COMP_TH0), 208 | 209 | ILEAD_OFF_6nA = 0x00, 210 | ILEAD_OFF_12nA = ILEAD_OFF0, 211 | ILEAD_OFF_18nA = ILEAD_OFF1, 212 | ILEAD_OFF_24nA = (ILEAD_OFF1 | ILEAD_OFF0), 213 | 214 | FLEAD_OFF_AC = FLEAD_OFF0, 215 | FLEAD_OFF_DC = (FLEAD_OFF1 | FLEAD_OFF0) 216 | }; 217 | 218 | enum CHnSET_bits { 219 | PDn = 0x80, 220 | PD_n = 0x80, 221 | GAINn2 = 0x40, 222 | GAINn1 = 0x20, 223 | GAINn0 = 0x10, 224 | MUXn2 = 0x04, 225 | MUXn1 = 0x02, 226 | MUXn0 = 0x01, 227 | 228 | CHnSET_const = 0x00, 229 | 230 | // ADS1299 231 | GAIN_1X = 0x00, 232 | GAIN_2X = GAINn0, 233 | GAIN_4X = GAINn1, 234 | GAIN_6X = (GAINn1 | GAINn0), 235 | GAIN_8X = GAINn2, 236 | GAIN_12X = (GAINn2 | GAINn0), 237 | GAIN_24X = (GAINn2 | GAINn1), 238 | 239 | // ADS1298 240 | ADS1298_GAIN_1X = GAINn0, 241 | ADS1298_GAIN_2X = GAINn1, 242 | ADS1298_GAIN_3X = (GAINn1 | GAINn0), 243 | ADS1298_GAIN_4X = GAINn2, 244 | ADS1298_GAIN_6X = 0x00, 245 | ADS1298_GAIN_8X = (GAINn2 | GAINn0), 246 | ADS1298_GAIN_12X = (GAINn2 | GAINn1), 247 | 248 | ELECTRODE_INPUT = 0x00, 249 | SHORTED = MUXn0, 250 | RLD_INPUT = MUXn1, 251 | MVDD = (MUXn1 | MUXn0), 252 | TEMP = MUXn2, 253 | TEST_SIGNAL = (MUXn2 | MUXn0), 254 | RLD_DRP = (MUXn2 | MUXn1), 255 | RLD_DRN = (MUXn2 | MUXn1 | MUXn0) 256 | }; 257 | 258 | enum CH1SET_bits { 259 | PD_1 = 0x80, 260 | GAIN12 = 0x40, 261 | GAIN11 = 0x20, 262 | GAIN10 = 0x10, 263 | MUX12 = 0x04, 264 | MUX11 = 0x02, 265 | MUX10 = 0x01, 266 | 267 | CH1SET_const = 0x00 268 | }; 269 | 270 | enum CH2SET_bits { 271 | PD_2 = 0x80, 272 | GAIN22 = 0x40, 273 | GAIN21 = 0x20, 274 | GAIN20 = 0x10, 275 | MUX22 = 0x04, 276 | MUX21 = 0x02, 277 | MUX20 = 0x01, 278 | 279 | CH2SET_const = 0x00 280 | }; 281 | 282 | enum CH3SET_bits { 283 | PD_3 = 0x80, 284 | GAIN32 = 0x40, 285 | GAIN31 = 0x20, 286 | GAIN30 = 0x10, 287 | MUX32 = 0x04, 288 | MUX31 = 0x02, 289 | MUX30 = 0x01, 290 | 291 | CH3SET_const = 0x00 292 | }; 293 | 294 | enum CH4SET_bits { 295 | PD_4 = 0x80, 296 | GAIN42 = 0x40, 297 | GAIN41 = 0x20, 298 | GAIN40 = 0x10, 299 | MUX42 = 0x04, 300 | MUX41 = 0x02, 301 | MUX40 = 0x01, 302 | 303 | CH4SET_const = 0x00 304 | }; 305 | 306 | enum CH5SET_bits { 307 | PD_5 = 0x80, 308 | GAIN52 = 0x40, 309 | GAIN51 = 0x20, 310 | GAIN50 = 0x10, 311 | MUX52 = 0x04, 312 | MUX51 = 0x02, 313 | MUX50 = 0x01, 314 | 315 | CH5SET_const = 0x00 316 | }; 317 | 318 | enum CH6SET_bits { 319 | PD_6 = 0x80, 320 | GAIN62 = 0x40, 321 | GAIN61 = 0x20, 322 | GAIN60 = 0x10, 323 | MUX62 = 0x04, 324 | MUX61 = 0x02, 325 | MUX60 = 0x01, 326 | 327 | CH6SET_const = 0x00 328 | }; 329 | 330 | enum CH7SET_bits { 331 | PD_7 = 0x80, 332 | GAIN72 = 0x40, 333 | GAIN71 = 0x20, 334 | GAIN70 = 0x10, 335 | MUX72 = 0x04, 336 | MUX71 = 0x02, 337 | MUX70 = 0x01, 338 | 339 | CH7SET_const = 0x00 340 | }; 341 | 342 | enum CH8SET_bits { 343 | PD_8 = 0x80, 344 | GAIN82 = 0x40, 345 | GAIN81 = 0x20, 346 | GAIN80 = 0x10, 347 | MUX82 = 0x04, 348 | MUX81 = 0x02, 349 | MUX80 = 0x01, 350 | 351 | CH8SET_const = 0x00 352 | }; 353 | 354 | enum RLD_SENSP_bits { 355 | RLD8P = 0x80, 356 | RLD7P = 0x40, 357 | RLD6P = 0x20, 358 | RLD5P = 0x10, 359 | RLD4P = 0x08, 360 | RLD3P = 0x04, 361 | RLD2P = 0x02, 362 | RLD1P = 0x01, 363 | 364 | RLD_SENSP_const = 0x00 365 | }; 366 | 367 | enum RLD_SENSN_bits { 368 | RLD8N = 0x80, 369 | RLD7N = 0x40, 370 | RLD6N = 0x20, 371 | RLD5N = 0x10, 372 | RLD4N = 0x08, 373 | RLD3N = 0x04, 374 | RLD2N = 0x02, 375 | RLD1N = 0x01, 376 | 377 | RLD_SENSN_const = 0x00 378 | }; 379 | 380 | enum LOFF_SENSP_bits { 381 | LOFF8P = 0x80, 382 | LOFF7P = 0x40, 383 | LOFF6P = 0x20, 384 | LOFF5P = 0x10, 385 | LOFF4P = 0x08, 386 | LOFF3P = 0x04, 387 | LOFF2P = 0x02, 388 | LOFF1P = 0x01, 389 | 390 | LOFF_SENSP_const = 0x00 391 | }; 392 | 393 | enum LOFF_SENSN_bits { 394 | LOFF8N = 0x80, 395 | LOFF7N = 0x40, 396 | LOFF6N = 0x20, 397 | LOFF5N = 0x10, 398 | LOFF4N = 0x08, 399 | LOFF3N = 0x04, 400 | LOFF2N = 0x02, 401 | LOFF1N = 0x01, 402 | 403 | LOFF_SENSN_const = 0x00 404 | }; 405 | 406 | enum LOFF_FLIP_bits { 407 | LOFF_FLIP8 = 0x80, 408 | LOFF_FLIP7 = 0x40, 409 | LOFF_FLIP6 = 0x20, 410 | LOFF_FLIP5 = 0x10, 411 | LOFF_FLIP4 = 0x08, 412 | LOFF_FLIP3 = 0x04, 413 | LOFF_FLIP2 = 0x02, 414 | LOFF_FLIP1 = 0x01, 415 | 416 | LOFF_FLIP_const = 0x00 417 | }; 418 | 419 | enum LOFF_STATP_bits { 420 | IN8P_OFF = 0x80, 421 | IN7P_OFF = 0x40, 422 | IN6P_OFF = 0x20, 423 | IN5P_OFF = 0x10, 424 | IN4P_OFF = 0x08, 425 | IN3P_OFF = 0x04, 426 | IN2P_OFF = 0x02, 427 | IN1P_OFF = 0x01, 428 | 429 | LOFF_STATP_const = 0x00 430 | }; 431 | 432 | enum LOFF_STATN_bits { 433 | IN8N_OFF = 0x80, 434 | IN7N_OFF = 0x40, 435 | IN6N_OFF = 0x20, 436 | IN5N_OFF = 0x10, 437 | IN4N_OFF = 0x08, 438 | IN3N_OFF = 0x04, 439 | IN2N_OFF = 0x02, 440 | IN1N_OFF = 0x01, 441 | 442 | LOFF_STATN_const = 0x00 443 | }; 444 | 445 | enum GPIO_bits { 446 | GPIOD4 = 0x80, 447 | GPIOD3 = 0x40, 448 | GPIOD2 = 0x20, 449 | GPIOD1 = 0x10, 450 | GPIOC4 = 0x08, 451 | GPIOC3 = 0x04, 452 | GPIOC2 = 0x02, 453 | GPIOC1 = 0x01, 454 | 455 | GPIO_const = 0x00 456 | }; 457 | 458 | enum PACE_bits { 459 | PACEE1 = 0x10, 460 | PACEE0 = 0x08, 461 | PACEO1 = 0x04, 462 | PACEO0 = 0x02, 463 | PD_PACE = 0x01, 464 | 465 | PACE_const = 0x00, 466 | 467 | PACEE_CHAN2 = 0x00, 468 | PACEE_CHAN4 = PACEE0, 469 | PACEE_CHAN6 = PACEE1, 470 | PACEE_CHAN8 = (PACEE1 | PACEE0), 471 | 472 | PACEO_CHAN1 = 0x00, 473 | PACEO_CHAN3 = PACEE0, 474 | PACEO_CHAN5 = PACEE1, 475 | PACEO_CHAN7 = (PACEE1 | PACEE0) 476 | }; 477 | 478 | enum RESP_bits { 479 | RESP_DEMOD_EN1 = 0x80, 480 | RESP_MOD_EN1 = 0x40, 481 | RESP_PH2 = 0x10, 482 | RESP_PH1 = 0x08, 483 | RESP_PH0 = 0x04, 484 | RESP_CTRL1 = 0x02, 485 | RESP_CTRL0 = 0x01, 486 | 487 | RESP_const = 0x20, 488 | 489 | RESP_PH_22_5 = 0x00, 490 | RESP_PH_45 = RESP_PH0, 491 | RESP_PH_67_5 = RESP_PH1, 492 | RESP_PH_90 = (RESP_PH1 | RESP_PH0), 493 | RESP_PH_112_5 = RESP_PH2, 494 | RESP_PH_135 = (RESP_PH2 | RESP_PH0), 495 | RESP_PH_157_5 = (RESP_PH2 | RESP_PH1), 496 | 497 | RESP_NONE = 0x00, 498 | RESP_EXT = RESP_CTRL0, 499 | RESP_INT_SIG_INT = RESP_CTRL1, 500 | RESP_INT_SIG_EXT = (RESP_CTRL1 | RESP_CTRL0) 501 | }; 502 | 503 | enum CONFIG4_bits { 504 | RESP_FREQ2 = 0x80, 505 | RESP_FREQ1 = 0x40, 506 | RESP_FREQ0 = 0x20, 507 | SINGLE_SHOT = 0x08, 508 | WCT_TO_RLD = 0x04, 509 | PD_LOFF_COMP = 0x02, 510 | 511 | CONFIG4_const = 0x00, 512 | 513 | RESP_FREQ_64k_Hz = 0x00, 514 | RESP_FREQ_32k_Hz = RESP_FREQ0, 515 | RESP_FREQ_16k_Hz = RESP_FREQ1, 516 | RESP_FREQ_8k_Hz = (RESP_FREQ1 | RESP_FREQ0), 517 | RESP_FREQ_4k_Hz = RESP_FREQ2, 518 | RESP_FREQ_2k_Hz = (RESP_FREQ2 | RESP_FREQ0), 519 | RESP_FREQ_1k_Hz = (RESP_FREQ2 | RESP_FREQ1), 520 | RESP_FREQ_500_Hz = (RESP_FREQ2 | RESP_FREQ1 | RESP_FREQ0) 521 | }; 522 | 523 | enum WCT1_bits { 524 | aVF_CH6 = 0x80, 525 | aVL_CH5 = 0x40, 526 | aVR_CH7 = 0x20, 527 | avR_CH4 = 0x10, 528 | PD_WCTA = 0x08, 529 | WCTA2 = 0x04, 530 | WCTA1 = 0x02, 531 | WCTA0 = 0x01, 532 | 533 | WCT1_const = 0x00, 534 | 535 | WCTA_CH1P = 0x00, 536 | WCTA_CH1N = WCTA0, 537 | WCTA_CH2P = WCTA1, 538 | WCTA_CH2N = (WCTA1 | WCTA0), 539 | WCTA_CH3P = WCTA2, 540 | WCTA_CH3N = (WCTA2 | WCTA0), 541 | WCTA_CH4P = (WCTA2 | WCTA1), 542 | WCTA_CH4N = (WCTA2 | WCTA1 | WCTA0) 543 | }; 544 | 545 | enum WCT2_bits { 546 | PD_WCTC = 0x80, 547 | PD_WCTB = 0x40, 548 | WCTB2 = 0x20, 549 | WCTB1 = 0x10, 550 | WCTB0 = 0x08, 551 | WCTC2 = 0x04, 552 | WCTC1 = 0x02, 553 | WCTC0 = 0x01, 554 | 555 | WCT2_const = 0x00, 556 | 557 | WCTB_CH1P = 0x00, 558 | WCTB_CH1N = WCTB0, 559 | WCTB_CH2P = WCTB1, 560 | WCTB_CH2N = (WCTB1 | WCTB0), 561 | WCTB_CH3P = WCTB2, 562 | WCTB_CH3N = (WCTB2 | WCTB0), 563 | WCTB_CH4P = (WCTB2 | WCTB1), 564 | WCTB_CH4N = (WCTB2 | WCTB1 | WCTB0), 565 | 566 | WCTC_CH1P = 0x00, 567 | WCTC_CH1N = WCTC0, 568 | WCTC_CH2P = WCTC1, 569 | WCTC_CH2N = (WCTC1 | WCTC0), 570 | WCTC_CH3P = WCTC2, 571 | WCTC_CH3N = (WCTC2 | WCTC0), 572 | WCTC_CH4P = (WCTC2 | WCTC1), 573 | WCTC_CH4N = (WCTC2 | WCTC1 | WCTC0) 574 | }; 575 | 576 | #ifdef __cplusplus 577 | } 578 | #endif /* namespace ADS129x */ 579 | 580 | #endif /* ADS129x_H */ 581 | -------------------------------------------------------------------------------- /hackeeg_driver/adsCommand.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * send and receive commands from TI ADS129x chips. 3 | * 4 | * Copyright (c) 2013 by Adam Feuer 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this library. If not, see . 18 | * 19 | */ 20 | 21 | #include "Arduino.h" 22 | #include "adsCommand.h" 23 | #include "ads129x.h" 24 | #include "SpiDma.h" 25 | 26 | /*void wait_for_drdy(int interval) 27 | { 28 | int i = 0; 29 | while (digitalRead(IPIN_DRDY) == HIGH) { 30 | if (i < interval) { 31 | continue; 32 | } 33 | i = 0; 34 | } 35 | }*/ 36 | 37 | void adcSendCommand(int cmd) { 38 | digitalWrite(PIN_CS, LOW); 39 | spiSend(cmd); 40 | delayMicroseconds(1); 41 | digitalWrite(PIN_CS, HIGH); 42 | } 43 | 44 | void adcSendCommandLeaveCsActive(int cmd) { 45 | digitalWrite(PIN_CS, LOW); 46 | spiSend(cmd); 47 | } 48 | 49 | void adcWreg(int reg, int val) { 50 | //see pages 40,43 of datasheet - 51 | digitalWrite(PIN_CS, LOW); 52 | spiSend(ADS129x::WREG | reg); 53 | delayMicroseconds(2); 54 | spiSend(0); // number of registers to be read/written – 1 55 | delayMicroseconds(2); 56 | spiSend(val); 57 | delayMicroseconds(1); 58 | digitalWrite(PIN_CS, HIGH); 59 | } 60 | 61 | int adcRreg(int reg) { 62 | uint8_t out = 0; 63 | digitalWrite(PIN_CS, LOW); 64 | spiSend(ADS129x::RREG | reg); 65 | delayMicroseconds(2); 66 | spiSend(0); // number of registers to be read/written – 1 67 | delayMicroseconds(2); 68 | out = spiRec(); 69 | delayMicroseconds(1); 70 | digitalWrite(PIN_CS, HIGH); 71 | return ((int) out); 72 | } 73 | -------------------------------------------------------------------------------- /hackeeg_driver/adsCommand.h: -------------------------------------------------------------------------------- 1 | /* 2 | * adsCommand.h 3 | * 4 | * Copyright (c) 2013-2019 by Adam Feuer 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this library. If not, see . 18 | * 19 | */ 20 | 21 | #ifndef _ADS_COMMAND_H 22 | #define _ADS_COMMAND_H 23 | 24 | #include "Arduino.h" 25 | 26 | // constants define pins on Arduino 27 | 28 | // Arduino Due 29 | // HackEEG Shield v1.5.0 30 | const int IPIN_PWDN = 33; 31 | const int PIN_CLKSEL = 48; 32 | const int IPIN_RESET = 47; 33 | 34 | const int PIN_START = 59; 35 | const int IPIN_DRDY = 24; // board 0: JP1, pos. 1 36 | //const int IPIN_DRDY = 25; // board 1: JP1, pos. 2 37 | //const int IPIN_DRDY = 26; // board 2: JP1, pos. 3 38 | //const int IPIN_DRDY = 27; // board 3: JP1, pos. 4 39 | 40 | const int PIN_CS = 23; // board 0: JP2, pos. 3 41 | //const int PIN_CS = 52; // board 1: JP2, pos. 4 42 | //const int PIN_CS = 10; // board 2: JP2, pos. 5 43 | //const int PIN_CS = 4; // board 3: JP2, pos. 6 44 | 45 | //const int PIN_DOUT = 11; //SPI out 46 | //const int PIN_DIN = 12; //SPI in 47 | //const int PIN_SCLK = 13; //SPI clock 48 | 49 | void adcWreg(int reg, int val); 50 | void adcSendCommand(int cmd); 51 | void adcSendCommandLeaveCsActive(int cmd); 52 | int adcRreg(int reg); 53 | 54 | #endif // _ADS_COMMAND_H 55 | -------------------------------------------------------------------------------- /hackeeg_driver/hackeeg_driver.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Driver for TI ADS129x 3 | for Arduino Due and Arduino Mega2560 4 | 5 | Copyright (c) 2013-2019 by Adam Feuer 6 | 7 | This library is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this library. If not, see . 19 | 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include "adsCommand.h" 26 | #include "ads129x.h" 27 | #include "SerialCommand.h" 28 | #include "JsonCommand.h" 29 | #include "Base64.h" 30 | #include "SpiDma.h" 31 | 32 | 33 | #define BAUD_RATE 2000000 // WiredSerial ignores this and uses the maximum rate 34 | #define WiredSerial SerialUSB // use the Arduino Due's Native USB port 35 | 36 | #define SPI_BUFFER_SIZE 200 37 | #define OUTPUT_BUFFER_SIZE 1000 38 | 39 | #define TEXT_MODE 0 40 | #define JSONLINES_MODE 1 41 | #define MESSAGEPACK_MODE 2 42 | 43 | #define RESPONSE_OK 200 44 | #define RESPONSE_BAD_REQUEST 400 45 | #define UNRECOGNIZED_COMMAND 406 46 | #define RESPONSE_ERROR 500 47 | #define RESPONSE_NOT_IMPLEMENTED 501 48 | #define RESPONSE_NO_ACTIVE_CHANNELS 502 49 | 50 | const char *STATUS_TEXT_OK = "Ok"; 51 | const char *STATUS_TEXT_BAD_REQUEST = "Bad request"; 52 | const char *STATUS_TEXT_ERROR = "Error"; 53 | const char *STATUS_TEXT_NOT_IMPLEMENTED = "Not Implemented"; 54 | const char *STATUS_TEXT_NO_ACTIVE_CHANNELS = "No Active Channels"; 55 | 56 | int protocol_mode = TEXT_MODE; 57 | int max_channels = 0; 58 | int num_active_channels = 0; 59 | boolean active_channels[9]; // reports whether channels 1..9 are active 60 | int num_spi_bytes = 0; 61 | int num_timestamped_spi_bytes = 0; 62 | boolean is_rdatac = false; 63 | boolean base64_mode = true; 64 | 65 | char hexDigits[] = "0123456789ABCDEF"; 66 | 67 | // microseconds timestamp 68 | #define TIMESTAMP_SIZE_IN_BYTES 4 69 | union { 70 | char timestamp_bytes[TIMESTAMP_SIZE_IN_BYTES]; 71 | unsigned long timestamp; 72 | } timestamp_union; 73 | 74 | // sample number counter 75 | #define SAMPLE_NUMBER_SIZE_IN_BYTES 4 76 | union { 77 | char sample_number_bytes[SAMPLE_NUMBER_SIZE_IN_BYTES]; 78 | unsigned long sample_number = 0; 79 | } sample_number_union; 80 | 81 | // SPI input buffer 82 | uint8_t spi_bytes[SPI_BUFFER_SIZE]; 83 | uint8_t spi_data_available; 84 | 85 | // char buffer to send via USB 86 | char output_buffer[OUTPUT_BUFFER_SIZE]; 87 | 88 | const char *hardware_type = "unknown"; 89 | const char *board_name = "HackEEG"; 90 | const char *maker_name = "Starcat LLC"; 91 | const char *driver_version = "v0.3.0"; 92 | 93 | 94 | const char *json_rdatac_header= "{\"C\":200,\"D\":\""; 95 | const char *json_rdatac_footer= "\"}"; 96 | 97 | uint8_t messagepack_rdatac_header[] = { 0x82, 0xa1, 0x43, 0xcc, 0xc8, 0xa1, 0x44, 0xc4}; 98 | size_t messagepack_rdatac_header_size = sizeof(messagepack_rdatac_header); 99 | 100 | SerialCommand serialCommand; 101 | JsonCommand jsonCommand; 102 | 103 | void arduinoSetup(); 104 | void adsSetup(); 105 | void detectActiveChannels(); 106 | void unrecognized(const char *); 107 | void unrecognizedJsonLines(const char *); 108 | 109 | void nopCommand(unsigned char unused1, unsigned char unused2); 110 | void versionCommand(unsigned char unused1, unsigned char unused2); 111 | void statusCommand(unsigned char unused1, unsigned char unused2); 112 | void serialNumberCommand(unsigned char unused1, unsigned char unused2); 113 | void textCommand(unsigned char unused1, unsigned char unused2); 114 | void jsonlinesCommand(unsigned char unused1, unsigned char unused2); 115 | void messagepackCommand(unsigned char unused1, unsigned char unused2); 116 | void ledOnCommand(unsigned char unused1, unsigned char unused2); 117 | void ledOffCommand(unsigned char unused1, unsigned char unused2); 118 | void boardLedOffCommand(unsigned char unused1, unsigned char unused2); 119 | void boardLedOnCommand(unsigned char unused1, unsigned char unused2); 120 | void wakeupCommand(unsigned char unused1, unsigned char unused2); 121 | void standbyCommand(unsigned char unused1, unsigned char unused2); 122 | void resetCommand(unsigned char unused1, unsigned char unused2); 123 | void startCommand(unsigned char unused1, unsigned char unused2); 124 | void stopCommand(unsigned char unused1, unsigned char unused2); 125 | void rdatacCommand(unsigned char unused1, unsigned char unused2); 126 | void sdatacCommand(unsigned char unused1, unsigned char unused2); 127 | void rdataCommand(unsigned char unused1, unsigned char unused2); 128 | void base64ModeOnCommand(unsigned char unused1, unsigned char unused2); 129 | void hexModeOnCommand(unsigned char unused1, unsigned char unused2); 130 | void helpCommand(unsigned char unused1, unsigned char unused2); 131 | void readRegisterCommand(unsigned char unused1, unsigned char unused2); 132 | void writeRegisterCommand(unsigned char unused1, unsigned char unused2); 133 | void readRegisterCommandDirect(unsigned char register_number); 134 | void writeRegisterCommandDirect(unsigned char register_number, unsigned char register_value); 135 | 136 | 137 | void setup() { 138 | WiredSerial.begin(BAUD_RATE); 139 | pinMode(PIN_LED, OUTPUT); // Configure the onboard LED for output 140 | digitalWrite(PIN_LED, LOW); // default to LED off 141 | 142 | protocol_mode = TEXT_MODE; 143 | arduinoSetup(); 144 | adsSetup(); 145 | 146 | // Setup callbacks for SerialCommand commands 147 | serialCommand.addCommand("nop", nopCommand); // No operation (does nothing) 148 | serialCommand.addCommand("micros", microsCommand); // Returns number of microseconds since the program began executing 149 | serialCommand.addCommand("version", versionCommand); // Echos the driver version number 150 | serialCommand.addCommand("status", statusCommand); // Echos the driver status 151 | serialCommand.addCommand("serialnumber", serialNumberCommand); // Echos the board serial number (UUID from the onboard 24AA256UID-I/SN I2S EEPROM) 152 | serialCommand.addCommand("text", textCommand); // Sets the communication protocol to text 153 | serialCommand.addCommand("jsonlines", jsonlinesCommand); // Sets the communication protocol to JSONLines 154 | serialCommand.addCommand("messagepack", messagepackCommand); // Sets the communication protocol to MessagePack 155 | serialCommand.addCommand("ledon", ledOnCommand); // Turns Arduino Due onboard LED on 156 | serialCommand.addCommand("ledoff", ledOffCommand); // Turns Arduino Due onboard LED off 157 | serialCommand.addCommand("boardledoff", boardLedOffCommand); // Turns HackEEG ADS1299 GPIO4 LED off 158 | serialCommand.addCommand("boardledon", boardLedOnCommand); // Turns HackEEG ADS1299 GPIO4 LED on 159 | serialCommand.addCommand("wakeup", wakeupCommand); // Send the WAKEUP command 160 | serialCommand.addCommand("standby", standbyCommand); // Send the STANDBY command 161 | serialCommand.addCommand("reset", resetCommand); // Reset the ADS1299 162 | serialCommand.addCommand("start", startCommand); // Send START command 163 | serialCommand.addCommand("stop", stopCommand); // Send STOP command 164 | serialCommand.addCommand("rdatac", rdatacCommand); // Enter read data continuous mode, clear the ringbuffer, and read new data into the ringbuffer 165 | serialCommand.addCommand("sdatac", sdatacCommand); // Stop read data continuous mode; ringbuffer data is still available 166 | serialCommand.addCommand("rdata", rdataCommand); // Read one sample of data from each active channel 167 | serialCommand.addCommand("rreg", readRegisterCommand); // Read ADS129x register, argument in hex, print contents in hex 168 | serialCommand.addCommand("wreg", writeRegisterCommand); // Write ADS129x register, arguments in hex 169 | serialCommand.addCommand("base64", base64ModeOnCommand); // RDATA commands send base64 encoded data - default 170 | serialCommand.addCommand("hex", hexModeOnCommand); // RDATA commands send hex encoded data 171 | serialCommand.addCommand("help", helpCommand); // Print list of commands 172 | serialCommand.setDefaultHandler(unrecognized); // Handler for any command that isn't matched 173 | 174 | // Setup callbacks for JsonCommand commands 175 | jsonCommand.addCommand("nop", nopCommand); // No operation (does nothing) 176 | jsonCommand.addCommand("micros", microsCommand); // Returns number of microseconds since the program began executing 177 | jsonCommand.addCommand("version", versionCommand); // Returns the driver version number 178 | jsonCommand.addCommand("ledon", ledOnCommand); // Turns Arduino Due onboard LED on 179 | jsonCommand.addCommand("ledoff", ledOffCommand); // Turns Arduino Due onboard LED off 180 | jsonCommand.addCommand("boardledoff", boardLedOffCommand); // Turns HackEEG ADS1299 GPIO4 LED off 181 | jsonCommand.addCommand("boardledon", boardLedOnCommand); // Turns HackEEG ADS1299 GPIO4 LED on 182 | jsonCommand.addCommand("status", statusCommand); // Returns the driver status 183 | jsonCommand.addCommand("reset", resetCommand); // Reset the ADS1299 184 | jsonCommand.addCommand("start", startCommand); // Send START command 185 | jsonCommand.addCommand("stop", stopCommand); // Send STOP command 186 | jsonCommand.addCommand("rdatac", rdatacCommand); // Enter read data continuous mode, clear the ringbuffer, and read new data into the ringbuffer 187 | jsonCommand.addCommand("sdatac", sdatacCommand); // Stop read data continuous mode; ringbuffer data is still available 188 | jsonCommand.addCommand("serialnumber", serialNumberCommand); // Returns the board serial number (UUID from the onboard 24AA256UID-I/SN I2S EEPROM) 189 | jsonCommand.addCommand("text", textCommand); // Sets the communication protocol to text 190 | jsonCommand.addCommand("jsonlines", jsonlinesCommand); // Sets the communication protocol to JSONLines 191 | jsonCommand.addCommand("messagepack", messagepackCommand); // Sets the communication protocol to MessagePack 192 | jsonCommand.addCommand("rreg", readRegisterCommandDirect); // Read ADS129x register 193 | jsonCommand.addCommand("wreg", writeRegisterCommandDirect); // Write ADS129x register 194 | jsonCommand.addCommand("rdata", rdataCommand); // Read one sample of data from each active channel 195 | jsonCommand.setDefaultHandler(unrecognizedJsonLines); // Handler for any command that isn't matched 196 | 197 | WiredSerial.println("Ready"); 198 | } 199 | 200 | void loop() { 201 | switch (protocol_mode) { 202 | case TEXT_MODE: 203 | serialCommand.readSerial(); 204 | break; 205 | case JSONLINES_MODE: 206 | case MESSAGEPACK_MODE: 207 | jsonCommand.readSerial(); 208 | break; 209 | default: 210 | // do nothing 211 | ; 212 | } 213 | send_samples(); 214 | } 215 | 216 | long hex_to_long(char *digits) { 217 | using namespace std; 218 | char *error; 219 | long n = strtol(digits, &error, 16); 220 | if (*error != 0) { 221 | return -1; // error 222 | } else { 223 | return n; 224 | } 225 | } 226 | 227 | void output_hex_byte(int value) { 228 | int clipped = value & 0xff; 229 | char charValue[3]; 230 | sprintf(charValue, "%02X", clipped); 231 | WiredSerial.print(charValue); 232 | } 233 | 234 | void encode_hex(char *output, char *input, int input_len) { 235 | register int count = 0; 236 | for (register int i = 0; i < input_len; i++) { 237 | register uint8_t low_nybble = input[i] & 0x0f; 238 | register uint8_t highNybble = input[i] >> 4; 239 | output[count++] = hexDigits[highNybble]; 240 | output[count++] = hexDigits[low_nybble]; 241 | } 242 | output[count] = 0; 243 | } 244 | 245 | void send_response_ok() { 246 | send_response(RESPONSE_OK, STATUS_TEXT_OK); 247 | } 248 | 249 | void send_response_error() { 250 | send_response(RESPONSE_ERROR, STATUS_TEXT_ERROR); 251 | } 252 | 253 | void send_response(int status_code, const char *status_text) { 254 | switch (protocol_mode) { 255 | case TEXT_MODE: 256 | char response[128]; 257 | sprintf(response, "%d %s", status_code, status_text); 258 | WiredSerial.println(response); 259 | break; 260 | case JSONLINES_MODE: 261 | jsonCommand.sendJsonLinesResponse(status_code, (char *) status_text); 262 | break; 263 | case MESSAGEPACK_MODE: 264 | // all responses are in JSON Lines, MessagePack mode is only for sending samples 265 | jsonCommand.sendJsonLinesResponse(status_code, (char *) status_text); 266 | break; 267 | default: 268 | // unknown protocol 269 | ; 270 | } 271 | } 272 | 273 | void send_jsonlines_data(int status_code, char data, char *status_text) { 274 | StaticJsonDocument<1024> doc; 275 | JsonObject root = doc.to(); 276 | root[STATUS_CODE_KEY] = status_code; 277 | root[STATUS_TEXT_KEY] = status_text; 278 | root[DATA_KEY] = data; 279 | serializeJson(doc, WiredSerial); 280 | WiredSerial.println(); 281 | doc.clear(); 282 | } 283 | 284 | void versionCommand(unsigned char unused1, unsigned char unused2) { 285 | send_response(RESPONSE_OK, driver_version); 286 | } 287 | 288 | void statusCommand(unsigned char unused1, unsigned char unused2) { 289 | detectActiveChannels(); 290 | if (protocol_mode == TEXT_MODE) { 291 | WiredSerial.println("200 Ok"); 292 | WiredSerial.print("Driver version: "); 293 | WiredSerial.println(driver_version); 294 | WiredSerial.print("Board name: "); 295 | WiredSerial.println(board_name); 296 | WiredSerial.print("Board maker: "); 297 | WiredSerial.println(maker_name); 298 | WiredSerial.print("Hardware type: "); 299 | WiredSerial.println(hardware_type); 300 | WiredSerial.print("Max channels: "); 301 | WiredSerial.println(max_channels); 302 | WiredSerial.print("Number of active channels: "); 303 | WiredSerial.println(num_active_channels); 304 | WiredSerial.println(); 305 | return; 306 | } 307 | StaticJsonDocument<1024> doc; 308 | JsonObject root = doc.to(); 309 | root[STATUS_CODE_KEY] = STATUS_OK; 310 | root[STATUS_TEXT_KEY] = STATUS_TEXT_OK; 311 | JsonObject status_info = root.createNestedObject(DATA_KEY); 312 | status_info["driver_version"] = driver_version; 313 | status_info["board_name"] = board_name; 314 | status_info["maker_name"] = maker_name; 315 | status_info["hardware_type"] = hardware_type; 316 | status_info["max_channels"] = max_channels; 317 | status_info["active_channels"] = num_active_channels; 318 | switch (protocol_mode) { 319 | case JSONLINES_MODE: 320 | case MESSAGEPACK_MODE: 321 | jsonCommand.sendJsonLinesDocResponse(doc); 322 | break; 323 | default: 324 | // unknown protocol 325 | ; 326 | } 327 | } 328 | 329 | void nopCommand(unsigned char unused1, unsigned char unused2) { 330 | send_response_ok(); 331 | } 332 | 333 | void microsCommand(unsigned char unused1, unsigned char unused2) { 334 | unsigned long microseconds = micros(); 335 | if (protocol_mode == TEXT_MODE) { 336 | send_response_ok(); 337 | WiredSerial.println(microseconds); 338 | return; 339 | } 340 | StaticJsonDocument<1024> doc; 341 | JsonObject root = doc.to(); 342 | root[STATUS_CODE_KEY] = STATUS_OK; 343 | root[STATUS_TEXT_KEY] = STATUS_TEXT_OK; 344 | root[DATA_KEY] = microseconds; 345 | switch (protocol_mode) { 346 | case JSONLINES_MODE: 347 | case MESSAGEPACK_MODE: 348 | jsonCommand.sendJsonLinesDocResponse(doc); 349 | break; 350 | default: 351 | // unknown protocol 352 | ; 353 | } 354 | } 355 | 356 | void serialNumberCommand(unsigned char unused1, unsigned char unused2) { 357 | send_response(RESPONSE_NOT_IMPLEMENTED, STATUS_TEXT_NOT_IMPLEMENTED); 358 | } 359 | 360 | void textCommand(unsigned char unused1, unsigned char unused2) { 361 | protocol_mode = TEXT_MODE; 362 | send_response_ok(); 363 | } 364 | 365 | void jsonlinesCommand(unsigned char unused1, unsigned char unused2) { 366 | protocol_mode = JSONLINES_MODE; 367 | send_response_ok(); 368 | } 369 | 370 | void messagepackCommand(unsigned char unused1, unsigned char unused2) { 371 | protocol_mode = MESSAGEPACK_MODE; 372 | send_response_ok(); 373 | } 374 | 375 | void ledOnCommand(unsigned char unused1, unsigned char unused2) { 376 | digitalWrite(PIN_LED, HIGH); 377 | send_response_ok(); 378 | } 379 | 380 | void ledOffCommand(unsigned char unused1, unsigned char unused2) { 381 | digitalWrite(PIN_LED, LOW); 382 | send_response_ok(); 383 | } 384 | 385 | void boardLedOnCommand(unsigned char unused1, unsigned char unused2) { 386 | int state = adcRreg(ADS129x::GPIO); 387 | state = state & 0xF7; 388 | state = state | 0x80; 389 | adcWreg(ADS129x::GPIO, state); 390 | send_response_ok(); 391 | } 392 | 393 | void boardLedOffCommand(unsigned char unused1, unsigned char unused2) { 394 | int state = adcRreg(ADS129x::GPIO); 395 | state = state & 0x77; 396 | adcWreg(ADS129x::GPIO, state); 397 | send_response_ok(); 398 | } 399 | 400 | void base64ModeOnCommand(unsigned char unused1, unsigned char unused2) { 401 | base64_mode = true; 402 | send_response(RESPONSE_OK, "Base64 mode on - rdata command will respond with base64 encoded data."); 403 | } 404 | 405 | void hexModeOnCommand(unsigned char unused1, unsigned char unused2) { 406 | base64_mode = false; 407 | send_response(RESPONSE_OK, "Hex mode on - rdata command will respond with hex encoded data"); 408 | } 409 | 410 | void helpCommand(unsigned char unused1, unsigned char unused2) { 411 | if (protocol_mode == JSONLINES_MODE || protocol_mode == MESSAGEPACK_MODE) { 412 | send_response(RESPONSE_OK, "Help not available in JSON Lines or MessagePack modes."); 413 | } else { 414 | WiredSerial.println("200 Ok"); 415 | WiredSerial.println("Available commands: "); 416 | serialCommand.printCommands(); 417 | WiredSerial.println(); 418 | } 419 | } 420 | 421 | void readRegisterCommand(unsigned char unused1, unsigned char unused2) { 422 | using namespace ADS129x; 423 | char *arg1; 424 | arg1 = serialCommand.next(); 425 | if (arg1 != NULL) { 426 | long registerNumber = hex_to_long(arg1); 427 | if (registerNumber >= 0) { 428 | int result = adcRreg(registerNumber); 429 | WiredSerial.print("200 Ok"); 430 | WiredSerial.print(" (Read Register "); 431 | output_hex_byte(registerNumber); 432 | WiredSerial.print(") "); 433 | WiredSerial.println(); 434 | output_hex_byte(result); 435 | WiredSerial.println(); 436 | } else { 437 | WiredSerial.println("402 Error: expected hexidecimal digits."); 438 | } 439 | } else { 440 | WiredSerial.println("403 Error: register argument missing."); 441 | } 442 | WiredSerial.println(); 443 | } 444 | 445 | void readRegisterCommandDirect(unsigned char register_number, unsigned char unused1) { 446 | using namespace ADS129x; 447 | if (register_number >= 0 and register_number <= 255) { 448 | unsigned char result = adcRreg(register_number); 449 | StaticJsonDocument<1024> doc; 450 | JsonObject root = doc.to(); 451 | root[STATUS_CODE_KEY] = STATUS_OK; 452 | root[STATUS_TEXT_KEY] = STATUS_TEXT_OK; 453 | root[DATA_KEY] = result; 454 | jsonCommand.sendJsonLinesDocResponse(doc); 455 | } else { 456 | send_response_error(); 457 | } 458 | } 459 | 460 | void writeRegisterCommand(unsigned char unused1, unsigned char unused2) { 461 | char *arg1, *arg2; 462 | arg1 = serialCommand.next(); 463 | arg2 = serialCommand.next(); 464 | if (arg1 != NULL) { 465 | if (arg2 != NULL) { 466 | long registerNumber = hex_to_long(arg1); 467 | long registerValue = hex_to_long(arg2); 468 | if (registerNumber >= 0 && registerValue >= 0) { 469 | adcWreg(registerNumber, registerValue); 470 | WiredSerial.print("200 Ok"); 471 | WiredSerial.print(" (Write Register "); 472 | output_hex_byte(registerNumber); 473 | WiredSerial.print(" "); 474 | output_hex_byte(registerValue); 475 | WiredSerial.print(") "); 476 | WiredSerial.println(); 477 | } else { 478 | WiredSerial.println("402 Error: expected hexidecimal digits."); 479 | } 480 | } else { 481 | WiredSerial.println("404 Error: value argument missing."); 482 | } 483 | } else { 484 | WiredSerial.println("403 Error: register argument missing."); 485 | } 486 | WiredSerial.println(); 487 | } 488 | 489 | 490 | void writeRegisterCommandDirect(unsigned char register_number, unsigned char register_value) { 491 | if (register_number >= 0 && register_value >= 0) { 492 | adcWreg(register_number, register_value); 493 | send_response_ok(); 494 | } else { 495 | send_response_error(); 496 | } 497 | } 498 | 499 | void wakeupCommand(unsigned char unused1, unsigned char unused2) { 500 | using namespace ADS129x; 501 | adcSendCommand(WAKEUP); 502 | send_response_ok(); 503 | } 504 | 505 | void standbyCommand(unsigned char unused1, unsigned char unused2) { 506 | using namespace ADS129x; 507 | adcSendCommand(STANDBY); 508 | send_response_ok(); 509 | } 510 | 511 | void resetCommand(unsigned char unused1, unsigned char unused2) { 512 | using namespace ADS129x; 513 | adcSendCommand(RESET); 514 | adsSetup(); 515 | send_response_ok(); 516 | } 517 | 518 | void startCommand(unsigned char unused1, unsigned char unused2) { 519 | using namespace ADS129x; 520 | adcSendCommand(START); 521 | sample_number_union.sample_number = 0; 522 | send_response_ok(); 523 | } 524 | 525 | void stopCommand(unsigned char unused1, unsigned char unused2) { 526 | using namespace ADS129x; 527 | adcSendCommand(STOP); 528 | send_response_ok(); 529 | } 530 | 531 | void rdataCommand(unsigned char unused1, unsigned char unused2) { 532 | using namespace ADS129x; 533 | while (digitalRead(IPIN_DRDY) == HIGH); 534 | adcSendCommandLeaveCsActive(RDATA); 535 | if (protocol_mode == TEXT_MODE) { 536 | send_response_ok(); 537 | } 538 | send_sample(); 539 | } 540 | 541 | void rdatacCommand(unsigned char unused1, unsigned char unused2) { 542 | using namespace ADS129x; 543 | detectActiveChannels(); 544 | if (num_active_channels > 0) { 545 | is_rdatac = true; 546 | adcSendCommand(RDATAC); 547 | send_response_ok(); 548 | } else { 549 | send_response(RESPONSE_NO_ACTIVE_CHANNELS, STATUS_TEXT_NO_ACTIVE_CHANNELS); 550 | } 551 | } 552 | 553 | void sdatacCommand(unsigned char unused1, unsigned char unused2) { 554 | using namespace ADS129x; 555 | is_rdatac = false; 556 | adcSendCommand(SDATAC); 557 | using namespace ADS129x; 558 | send_response_ok(); 559 | } 560 | 561 | // This gets set as the default handler, and gets called when no other command matches. 562 | void unrecognized(const char *command) { 563 | WiredSerial.println("406 Error: Unrecognized command."); 564 | WiredSerial.println(); 565 | } 566 | 567 | // This gets set as the default handler for jsonlines and messagepack, and gets called when no other command matches. 568 | void unrecognizedJsonLines(const char *command) { 569 | StaticJsonDocument<1024> doc; 570 | JsonObject root = doc.to(); 571 | root[STATUS_CODE_KEY] = UNRECOGNIZED_COMMAND; 572 | root[STATUS_TEXT_KEY] = "Unrecognized command"; 573 | jsonCommand.sendJsonLinesDocResponse(doc); 574 | } 575 | 576 | void detectActiveChannels() { //set device into RDATAC (continous) mode -it will stream data 577 | if ((is_rdatac) || (max_channels < 1)) return; //we can not read registers when in RDATAC mode 578 | //Serial.println("Detect active channels: "); 579 | using namespace ADS129x; 580 | num_active_channels = 0; 581 | for (int i = 1; i <= max_channels; i++) { 582 | delayMicroseconds(1); 583 | int chSet = adcRreg(CHnSET + i); 584 | active_channels[i] = ((chSet & 7) != SHORTED); 585 | if ((chSet & 7) != SHORTED) num_active_channels++; 586 | } 587 | } 588 | 589 | void drdy_interrupt() { 590 | spi_data_available = 1; 591 | } 592 | 593 | inline void send_samples(void) { 594 | if (!is_rdatac) return; 595 | if (spi_data_available) { 596 | spi_data_available = 0; 597 | receive_sample(); 598 | send_sample(); 599 | } 600 | } 601 | 602 | inline void receive_sample() { 603 | digitalWrite(PIN_CS, LOW); 604 | delayMicroseconds(10); 605 | memset(spi_bytes, 0, sizeof(spi_bytes)); 606 | timestamp_union.timestamp = micros(); 607 | spi_bytes[0] = timestamp_union.timestamp_bytes[0]; 608 | spi_bytes[1] = timestamp_union.timestamp_bytes[1]; 609 | spi_bytes[2] = timestamp_union.timestamp_bytes[2]; 610 | spi_bytes[3] = timestamp_union.timestamp_bytes[3]; 611 | spi_bytes[4] = sample_number_union.sample_number_bytes[0]; 612 | spi_bytes[5] = sample_number_union.sample_number_bytes[1]; 613 | spi_bytes[6] = sample_number_union.sample_number_bytes[2]; 614 | spi_bytes[7] = sample_number_union.sample_number_bytes[3]; 615 | 616 | uint8_t returnCode = spiRec(spi_bytes + TIMESTAMP_SIZE_IN_BYTES + SAMPLE_NUMBER_SIZE_IN_BYTES, num_spi_bytes); 617 | 618 | digitalWrite(PIN_CS, HIGH); 619 | sample_number_union.sample_number++; 620 | } 621 | 622 | inline void send_sample(void) { 623 | switch (protocol_mode) { 624 | case JSONLINES_MODE: 625 | WiredSerial.write(json_rdatac_header); 626 | base64_encode(output_buffer, (char *) spi_bytes, num_timestamped_spi_bytes); 627 | WiredSerial.write(output_buffer); 628 | WiredSerial.write(json_rdatac_footer); 629 | WiredSerial.write("\n"); 630 | break; 631 | case TEXT_MODE: 632 | if (base64_mode) { 633 | base64_encode(output_buffer, (char *) spi_bytes, num_timestamped_spi_bytes); 634 | } else { 635 | encode_hex(output_buffer, (char *) spi_bytes, num_timestamped_spi_bytes); 636 | } 637 | WiredSerial.println(output_buffer); 638 | break; 639 | case MESSAGEPACK_MODE: 640 | send_sample_messagepack(num_timestamped_spi_bytes); 641 | break; 642 | } 643 | } 644 | 645 | 646 | inline void send_sample_json(int num_bytes) { 647 | StaticJsonDocument<1024> doc; 648 | JsonObject root = doc.to(); 649 | root[STATUS_CODE_KEY] = STATUS_OK; 650 | root[STATUS_TEXT_KEY] = STATUS_TEXT_OK; 651 | JsonArray data = root.createNestedArray(DATA_KEY); 652 | copyArray(spi_bytes, num_bytes, data); 653 | jsonCommand.sendJsonLinesDocResponse(doc); 654 | } 655 | 656 | 657 | inline void send_sample_messagepack(int num_bytes) { 658 | WiredSerial.write(messagepack_rdatac_header, messagepack_rdatac_header_size); 659 | WiredSerial.write((uint8_t) num_bytes); 660 | WiredSerial.write(spi_bytes, num_bytes); 661 | } 662 | 663 | void adsSetup() { //default settings for ADS1298 and compatible chips 664 | using namespace ADS129x; 665 | // Send SDATAC Command (Stop Read Data Continuously mode) 666 | spi_data_available = 0; 667 | attachInterrupt(digitalPinToInterrupt(IPIN_DRDY), drdy_interrupt, FALLING); 668 | adcSendCommand(SDATAC); 669 | delay(1000); //pause to provide ads129n enough time to boot up... 670 | // delayMicroseconds(2); 671 | delay(100); 672 | int val = adcRreg(ID); 673 | switch (val & B00011111) { 674 | case B10000: 675 | hardware_type = "ADS1294"; 676 | max_channels = 4; 677 | break; 678 | case B10001: 679 | hardware_type = "ADS1296"; 680 | max_channels = 6; 681 | break; 682 | case B10010: 683 | hardware_type = "ADS1298"; 684 | max_channels = 8; 685 | break; 686 | case B11110: 687 | hardware_type = "ADS1299"; 688 | max_channels = 8; 689 | break; 690 | case B11100: 691 | hardware_type = "ADS1299-4"; 692 | max_channels = 4; 693 | break; 694 | case B11101: 695 | hardware_type = "ADS1299-6"; 696 | max_channels = 6; 697 | break; 698 | default: 699 | max_channels = 0; 700 | } 701 | num_spi_bytes = (3 * (max_channels + 1)); //24-bits header plus 24-bits per channel 702 | num_timestamped_spi_bytes = num_spi_bytes + TIMESTAMP_SIZE_IN_BYTES + SAMPLE_NUMBER_SIZE_IN_BYTES; 703 | if (max_channels == 0) { //error mode 704 | while (1) { 705 | digitalWrite(PIN_LED, HIGH); 706 | delay(500); 707 | digitalWrite(PIN_LED, LOW); 708 | delay(500); 709 | } 710 | } //error mode 711 | 712 | // All GPIO set to output 0x0000: (floating CMOS inputs can flicker on and off, creating noise) 713 | adcWreg(GPIO, 0); 714 | adcWreg(CONFIG3,PD_REFBUF | CONFIG3_const); 715 | digitalWrite(PIN_START, HIGH); 716 | } 717 | 718 | void arduinoSetup() { 719 | pinMode(PIN_LED, OUTPUT); 720 | using namespace ADS129x; 721 | // prepare pins to be outputs or inputs 722 | //pinMode(PIN_SCLK, OUTPUT); //optional - SPI library will do this for us 723 | //pinMode(PIN_DIN, OUTPUT); //optional - SPI library will do this for us 724 | //pinMode(PIN_DOUT, INPUT); //optional - SPI library will do this for us 725 | //pinMode(PIN_CS, OUTPUT); 726 | pinMode(PIN_START, OUTPUT); 727 | pinMode(IPIN_DRDY, INPUT); 728 | pinMode(PIN_CLKSEL, OUTPUT);// *optional 729 | pinMode(IPIN_RESET, OUTPUT);// *optional 730 | //pinMode(IPIN_PWDN, OUTPUT);// *optional 731 | digitalWrite(PIN_CLKSEL, HIGH); // internal clock 732 | //start Serial Peripheral Interface 733 | spiBegin(PIN_CS); 734 | spiInit(MSBFIRST, SPI_MODE1, SPI_CLOCK_DIVIDER); 735 | //Start ADS1298 736 | delay(500); //wait for the ads129n to be ready - it can take a while to charge caps 737 | digitalWrite(PIN_CLKSEL, HIGH);// *optional 738 | delay(10); // wait for oscillator to wake up 739 | digitalWrite(IPIN_PWDN, HIGH); // *optional - turn off power down mode 740 | digitalWrite(IPIN_RESET, HIGH); 741 | delay(1000); 742 | digitalWrite(IPIN_RESET, LOW); 743 | delay(1); 744 | digitalWrite(IPIN_RESET, HIGH); 745 | delay(1); // *optional Wait for 18 tCLKs AKA 9 microseconds, we use 1 millisecond 746 | } 747 | --------------------------------------------------------------------------------