├── .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 |
--------------------------------------------------------------------------------