├── .gitignore
├── LICENSE
├── README.md
└── components
└── uart
├── __init__.py
├── automation.h
├── switch
├── __init__.py
├── uart_switch.cpp
└── uart_switch.h
├── uart.cpp
├── uart.h
├── uart_component.cpp
├── uart_component.h
├── uart_component_esp32_arduino.cpp
├── uart_component_esp32_arduino.h
├── uart_component_esp8266.cpp
├── uart_component_esp8266.h
├── uart_component_esp_idf.cpp
├── uart_component_esp_idf.h
├── uart_debugger.cpp
└── uart_debugger.h
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-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 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 | cover/
54 |
55 | # Translations
56 | *.mo
57 | *.pot
58 |
59 | # Django stuff:
60 | *.log
61 | local_settings.py
62 | db.sqlite3
63 | db.sqlite3-journal
64 |
65 | # Flask stuff:
66 | instance/
67 | .webassets-cache
68 |
69 | # Scrapy stuff:
70 | .scrapy
71 |
72 | # Sphinx documentation
73 | docs/_build/
74 |
75 | # PyBuilder
76 | .pybuilder/
77 | target/
78 |
79 | # Jupyter Notebook
80 | .ipynb_checkpoints
81 |
82 | # IPython
83 | profile_default/
84 | ipython_config.py
85 |
86 | # pyenv
87 | # For a library or package, you might want to ignore these files since the code is
88 | # intended to run in multiple environments; otherwise, check them in:
89 | # .python-version
90 |
91 | # pipenv
92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
95 | # install all needed dependencies.
96 | #Pipfile.lock
97 |
98 | # poetry
99 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
100 | # This is especially recommended for binary packages to ensure reproducibility, and is more
101 | # commonly ignored for libraries.
102 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
103 | #poetry.lock
104 |
105 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
106 | __pypackages__/
107 |
108 | # Celery stuff
109 | celerybeat-schedule
110 | celerybeat.pid
111 |
112 | # SageMath parsed files
113 | *.sage.py
114 |
115 | # Environments
116 | .env
117 | .venv
118 | env/
119 | venv/
120 | ENV/
121 | env.bak/
122 | venv.bak/
123 |
124 | # Spyder project settings
125 | .spyderproject
126 | .spyproject
127 |
128 | # Rope project settings
129 | .ropeproject
130 |
131 | # mkdocs documentation
132 | /site
133 |
134 | # mypy
135 | .mypy_cache/
136 | .dmypy.json
137 | dmypy.json
138 |
139 | # Pyre type checker
140 | .pyre/
141 |
142 | # pytype static type analyzer
143 | .pytype/
144 |
145 | # Cython debug symbols
146 | cython_debug/
147 |
148 | # PyCharm
149 | # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
150 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
151 | # and can be added to the global gitignore or merged into this file. For a more nuclear
152 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
153 | #.idea/
154 |
155 | # Prerequisites
156 | *.d
157 |
158 | # Compiled Object files
159 | *.slo
160 | *.lo
161 | *.o
162 | *.obj
163 |
164 | # Precompiled Headers
165 | *.gch
166 | *.pch
167 |
168 | # Compiled Dynamic libraries
169 | *.so
170 | *.dylib
171 | *.dll
172 |
173 | # Fortran module files
174 | *.mod
175 | *.smod
176 |
177 | # Compiled Static libraries
178 | *.lai
179 | *.la
180 | *.a
181 | *.lib
182 |
183 | # Executables
184 | *.exe
185 | *.out
186 | *.app
187 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # shawly's ESPHome Components
2 |
3 | This repository contains my personal and some customized default components for [ESPHome](https://esphome.io/).
4 |
5 | ## 1. Usage
6 |
7 | Add the repository to [`external_components`](https://esphome.io/components/external_components.html) in your configuration like this.
8 |
9 | ```yaml
10 | external_components:
11 | - source: github://shawly/esphome-components
12 | ```
13 |
14 | ## 2. Components
15 |
16 | ### 2.1. `uart`
17 |
18 | This is a customized version of the [`uart`](https://esphome.io/components/uart.html) component which allows the use of hardware flow control for ESP32 boards.
19 | **You can only use this with the [ESP-IDF framework](https://esphome.io/components/esp32.html#esp32-espidf-framework) and ESP32 boards!**
20 |
21 | #### 2.1.1. Example
22 |
23 | ```yaml
24 | # required
25 | external_components:
26 | - source: github://shawly/esphome-components
27 | components: [uart]
28 |
29 | esp32:
30 | board: esp32dev
31 | framework:
32 | # this is also required
33 | type: esp-idf
34 | version: recommended
35 | sdkconfig_options:
36 | CONFIG_COMPILER_OPTIMIZATION_SIZE: y
37 |
38 | uart:
39 | tx_pin: GPIO21
40 | rx_pin: GPIO19
41 | baud_rate: 57600
42 | # you need to set rts & cts pins
43 | rts_pin: GPIO18
44 | cts_pin: GPIO5
45 | # possible values are
46 | # - DISABLE = disable hardware flow control
47 | # - RTS = enable RX hardware flow control (rts)
48 | # - CTS = enable TX hardware flow control (cts)
49 | # - CTS_RTS = enable hardware flow control
50 | # - MAX = ?
51 | hw_flowctrl: CTS_RTS
52 | debug:
53 | # this is just for debugging
54 | direction: BOTH
55 | dummy_receiver: true
56 | after:
57 | delimiter: "\n"
58 | sequence:
59 | - lambda: UARTDebug::log_string(direction, bytes);
60 |
61 | switch:
62 | - platform: uart
63 | name: "UART Test"
64 | data: "Hello World\n"
65 | ```
66 |
67 | #### 2.1.2. Notes
68 |
69 | To test hardware flow control you need a USB to TTL serial adapter with an FTDI FT232RL chip which allows you to use hardware flow control. It should have pins for RTS and CTS.
70 | These ones should work: [[1](https://www.amazon.com/dp/B07BBPX8B8)] (confirmed working) [[2](https://www.amazon.com/dp/B07XF2SLQ1)] (this one requires soldering pin headers for RTS/CTS)
71 | Remember to enable hardware flow control on your host with `stty -F /dev/ttyUSB0 crtscts` and don't forget to set the correct baudrate with `stty -F /dev/ttyUSB0 57600` (replace 57600 with your required baudrate)
72 |
--------------------------------------------------------------------------------
/components/uart/__init__.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | import esphome.codegen as cg
4 | import esphome.config_validation as cv
5 | import esphome.final_validate as fv
6 | from esphome.yaml_util import make_data_base
7 | from esphome import pins, automation
8 | from esphome.const import (
9 | CONF_BAUD_RATE,
10 | CONF_ID,
11 | CONF_NUMBER,
12 | CONF_RX_PIN,
13 | CONF_TX_PIN,
14 | CONF_UART_ID,
15 | CONF_DATA,
16 | CONF_RX_BUFFER_SIZE,
17 | CONF_INVERTED,
18 | CONF_INVERT,
19 | CONF_TRIGGER_ID,
20 | CONF_SEQUENCE,
21 | CONF_TIMEOUT,
22 | CONF_DEBUG,
23 | CONF_DIRECTION,
24 | CONF_AFTER,
25 | CONF_BYTES,
26 | CONF_DELIMITER,
27 | CONF_DUMMY_RECEIVER,
28 | CONF_DUMMY_RECEIVER_ID,
29 | CONF_LAMBDA,
30 | )
31 | from esphome.core import CORE
32 |
33 | CONF_CTS_PIN = "cts_pin"
34 | CONF_RTS_PIN = "rts_pin"
35 |
36 | CODEOWNERS = ["@esphome/core"]
37 | uart_ns = cg.esphome_ns.namespace("uart")
38 | UARTComponent = uart_ns.class_("UARTComponent")
39 |
40 | IDFUARTComponent = uart_ns.class_("IDFUARTComponent", UARTComponent, cg.Component)
41 | ESP32ArduinoUARTComponent = uart_ns.class_(
42 | "ESP32ArduinoUARTComponent", UARTComponent, cg.Component
43 | )
44 | ESP8266UartComponent = uart_ns.class_(
45 | "ESP8266UartComponent", UARTComponent, cg.Component
46 | )
47 |
48 | UARTDevice = uart_ns.class_("UARTDevice")
49 | UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action)
50 | UARTDebugger = uart_ns.class_("UARTDebugger", cg.Component, automation.Action)
51 | UARTDummyReceiver = uart_ns.class_("UARTDummyReceiver", cg.Component)
52 | MULTI_CONF = True
53 |
54 |
55 | def validate_raw_data(value):
56 | if isinstance(value, str):
57 | return value.encode("utf-8")
58 | if isinstance(value, str):
59 | return value
60 | if isinstance(value, list):
61 | return cv.Schema([cv.hex_uint8_t])(value)
62 | raise cv.Invalid(
63 | "data must either be a string wrapped in quotes or a list of bytes"
64 | )
65 |
66 |
67 | def validate_rx_pin(value):
68 | value = pins.internal_gpio_input_pin_schema(value)
69 | if CORE.is_esp8266 and value[CONF_NUMBER] >= 16:
70 | raise cv.Invalid("Pins GPIO16 and GPIO17 cannot be used as RX pins on ESP8266.")
71 | return value
72 |
73 |
74 | def validate_invert_esp32(config):
75 | if (
76 | CORE.is_esp32
77 | and CONF_TX_PIN in config
78 | and CONF_RX_PIN in config
79 | and config[CONF_TX_PIN][CONF_INVERTED] != config[CONF_RX_PIN][CONF_INVERTED]
80 | ):
81 | raise cv.Invalid(
82 | "Different invert values for TX and RX pin are not (yet) supported for ESP32."
83 | )
84 | return config
85 |
86 |
87 | def _uart_declare_type(value):
88 | if CORE.is_esp8266:
89 | return cv.declare_id(ESP8266UartComponent)(value)
90 | if CORE.is_esp32:
91 | if CORE.using_arduino:
92 | return cv.declare_id(ESP32ArduinoUARTComponent)(value)
93 | if CORE.using_esp_idf:
94 | return cv.declare_id(IDFUARTComponent)(value)
95 | raise NotImplementedError
96 |
97 |
98 | UARTParityOptions = uart_ns.enum("UARTParityOptions")
99 | UART_PARITY_OPTIONS = {
100 | "NONE": UARTParityOptions.UART_CONFIG_PARITY_NONE,
101 | "EVEN": UARTParityOptions.UART_CONFIG_PARITY_EVEN,
102 | "ODD": UARTParityOptions.UART_CONFIG_PARITY_ODD,
103 | }
104 |
105 | UARTHardwareFlowControl = uart_ns.enum("UARTHardwareFlowControl")
106 | UART_HW_FLOWCTRL_OPTIONS = {
107 | "DISABLE": UARTHardwareFlowControl.UART_CONFIG_HW_FLOWCTRL_DISABLE,
108 | "RTS": UARTHardwareFlowControl.UART_CONFIG_HW_FLOWCTRL_RTS,
109 | "CTS": UARTHardwareFlowControl.UART_CONFIG_HW_FLOWCTRL_CTS,
110 | "CTS_RTS": UARTHardwareFlowControl.UART_CONFIG_HW_FLOWCTRL_CTS_RTS,
111 | "MAX": UARTHardwareFlowControl.UART_CONFIG_HW_FLOWCTRL_MAX,
112 | }
113 |
114 | CONF_STOP_BITS = "stop_bits"
115 | CONF_DATA_BITS = "data_bits"
116 | CONF_PARITY = "parity"
117 | CONF_HW_FLOWCTRL = "hw_flowctrl"
118 |
119 | UARTDirection = uart_ns.enum("UARTDirection")
120 | UART_DIRECTIONS = {
121 | "RX": UARTDirection.UART_DIRECTION_RX,
122 | "TX": UARTDirection.UART_DIRECTION_TX,
123 | "BOTH": UARTDirection.UART_DIRECTION_BOTH,
124 | }
125 |
126 | # The reason for having CONF_BYTES at 150 by default:
127 | #
128 | # The log message buffer size is 512 bytes by default. About 35 bytes are
129 | # used for the log prefix. That leaves us with 477 bytes for logging data.
130 | # The default log output is hex, which uses 3 characters per represented
131 | # byte (2 hex chars + 1 separator). That means that 477 / 3 = 159 bytes
132 | # can be represented in a single log line. Using 150, because people love
133 | # round numbers.
134 | AFTER_DEFAULTS = {CONF_BYTES: 150, CONF_TIMEOUT: "100ms"}
135 |
136 | # By default, log in hex format when no specific sequence is provided.
137 | DEFAULT_DEBUG_OUTPUT = "UARTDebug::log_hex(direction, bytes, ':');"
138 | DEFAULT_SEQUENCE = [{CONF_LAMBDA: make_data_base(DEFAULT_DEBUG_OUTPUT)}]
139 |
140 |
141 | def maybe_empty_debug(value):
142 | if value is None:
143 | value = {}
144 | return DEBUG_SCHEMA(value)
145 |
146 |
147 | DEBUG_SCHEMA = cv.Schema(
148 | {
149 | cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UARTDebugger),
150 | cv.Optional(CONF_DIRECTION, default="BOTH"): cv.enum(
151 | UART_DIRECTIONS, upper=True
152 | ),
153 | cv.Optional(CONF_AFTER, default=AFTER_DEFAULTS): cv.Schema(
154 | {
155 | cv.Optional(
156 | CONF_BYTES, default=AFTER_DEFAULTS[CONF_BYTES]
157 | ): cv.validate_bytes,
158 | cv.Optional(
159 | CONF_TIMEOUT, default=AFTER_DEFAULTS[CONF_TIMEOUT]
160 | ): cv.positive_time_period_milliseconds,
161 | cv.Optional(CONF_DELIMITER): cv.templatable(validate_raw_data),
162 | }
163 | ),
164 | cv.Optional(
165 | CONF_SEQUENCE, default=DEFAULT_SEQUENCE
166 | ): automation.validate_automation(),
167 | cv.Optional(CONF_DUMMY_RECEIVER, default=False): cv.boolean,
168 | cv.GenerateID(CONF_DUMMY_RECEIVER_ID): cv.declare_id(UARTDummyReceiver),
169 | }
170 | )
171 |
172 | CONFIG_SCHEMA = cv.All(
173 | cv.Schema(
174 | {
175 | cv.GenerateID(): _uart_declare_type,
176 | cv.Required(CONF_BAUD_RATE): cv.int_range(min=1),
177 | cv.Optional(CONF_TX_PIN): pins.internal_gpio_output_pin_schema,
178 | cv.Optional(CONF_RX_PIN): validate_rx_pin,
179 | # TODO: validate to only allow this on esp32 idf framework
180 | cv.Optional(CONF_CTS_PIN): pins.internal_gpio_input_pin_schema,
181 | cv.Optional(CONF_RTS_PIN): pins.internal_gpio_output_pin_schema,
182 | cv.Optional(CONF_RX_BUFFER_SIZE, default=256): cv.validate_bytes,
183 | cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True),
184 | cv.Optional(CONF_DATA_BITS, default=8): cv.int_range(min=5, max=8),
185 | cv.Optional(CONF_PARITY, default="NONE"): cv.enum(
186 | UART_PARITY_OPTIONS, upper=True
187 | ),
188 | cv.Optional(CONF_HW_FLOWCTRL, default="DISABLE"): cv.enum(
189 | UART_HW_FLOWCTRL_OPTIONS, upper=True
190 | ),
191 | cv.Optional(CONF_INVERT): cv.invalid(
192 | "This option has been removed. Please instead use invert in the tx/rx pin schemas."
193 | ),
194 | cv.Optional(CONF_DEBUG): maybe_empty_debug,
195 | }
196 | ).extend(cv.COMPONENT_SCHEMA),
197 | cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN),
198 | validate_invert_esp32,
199 | )
200 |
201 |
202 | async def debug_to_code(config, parent):
203 | trigger = cg.new_Pvariable(config[CONF_TRIGGER_ID], parent)
204 | await cg.register_component(trigger, config)
205 | for action in config[CONF_SEQUENCE]:
206 | await automation.build_automation(
207 | trigger,
208 | [(UARTDirection, "direction"), (cg.std_vector.template(cg.uint8), "bytes")],
209 | action,
210 | )
211 | cg.add(trigger.set_direction(config[CONF_DIRECTION]))
212 | after = config[CONF_AFTER]
213 | cg.add(trigger.set_after_bytes(after[CONF_BYTES]))
214 | cg.add(trigger.set_after_timeout(after[CONF_TIMEOUT]))
215 | if CONF_DELIMITER in after:
216 | data = after[CONF_DELIMITER]
217 | if isinstance(data, bytes):
218 | data = list(data)
219 | for byte in after[CONF_DELIMITER]:
220 | cg.add(trigger.add_delimiter_byte(byte))
221 | if config[CONF_DUMMY_RECEIVER]:
222 | dummy = cg.new_Pvariable(config[CONF_DUMMY_RECEIVER_ID], parent)
223 | await cg.register_component(dummy, {})
224 | cg.add_define("USE_UART_DEBUGGER")
225 |
226 |
227 | async def to_code(config):
228 | cg.add_global(uart_ns.using)
229 | var = cg.new_Pvariable(config[CONF_ID])
230 | await cg.register_component(var, config)
231 |
232 | cg.add(var.set_baud_rate(config[CONF_BAUD_RATE]))
233 |
234 | if CONF_TX_PIN in config:
235 | tx_pin = await cg.gpio_pin_expression(config[CONF_TX_PIN])
236 | cg.add(var.set_tx_pin(tx_pin))
237 | if CONF_RX_PIN in config:
238 | rx_pin = await cg.gpio_pin_expression(config[CONF_RX_PIN])
239 | cg.add(var.set_rx_pin(rx_pin))
240 | if CONF_CTS_PIN in config:
241 | cts_pin = await cg.gpio_pin_expression(config[CONF_CTS_PIN])
242 | cg.add(var.set_cts_pin(cts_pin))
243 | if CONF_RTS_PIN in config:
244 | rts_pin = await cg.gpio_pin_expression(config[CONF_RTS_PIN])
245 | cg.add(var.set_rts_pin(rts_pin))
246 | cg.add(var.set_rx_buffer_size(config[CONF_RX_BUFFER_SIZE]))
247 | cg.add(var.set_stop_bits(config[CONF_STOP_BITS]))
248 | cg.add(var.set_data_bits(config[CONF_DATA_BITS]))
249 | cg.add(var.set_parity(config[CONF_PARITY]))
250 | cg.add(var.set_hw_flowctrl(config[CONF_HW_FLOWCTRL]))
251 |
252 | if CONF_DEBUG in config:
253 | await debug_to_code(config[CONF_DEBUG], var)
254 |
255 |
256 | # A schema to use for all UART devices, all UART integrations must extend this!
257 | UART_DEVICE_SCHEMA = cv.Schema(
258 | {
259 | cv.GenerateID(CONF_UART_ID): cv.use_id(UARTComponent),
260 | }
261 | )
262 |
263 | KEY_UART_DEVICES = "uart_devices"
264 |
265 |
266 | def final_validate_device_schema(
267 | name: str,
268 | *,
269 | baud_rate: Optional[int] = None,
270 | require_tx: bool = False,
271 | require_rx: bool = False,
272 | require_cts: bool = False,
273 | require_rts: bool = False,
274 | ):
275 | def validate_baud_rate(value):
276 | if value != baud_rate:
277 | raise cv.Invalid(
278 | f"Component {name} required baud rate {baud_rate} for the uart bus"
279 | )
280 | return value
281 |
282 | def validate_pin(opt, device):
283 | def validator(value):
284 | if opt in device:
285 | raise cv.Invalid(
286 | f"The uart {opt} is used both by {name} and {device[opt]}, "
287 | f"but can only be used by one. Please create a new uart bus for {name}."
288 | )
289 | device[opt] = name
290 | return value
291 |
292 | return validator
293 |
294 | def validate_hub(hub_config):
295 | hub_schema = {}
296 | uart_id = hub_config[CONF_ID]
297 | devices = fv.full_config.get().data.setdefault(KEY_UART_DEVICES, {})
298 | device = devices.setdefault(uart_id, {})
299 |
300 | if require_tx:
301 | hub_schema[
302 | cv.Required(
303 | CONF_TX_PIN,
304 | msg=f"Component {name} requires this uart bus to declare a tx_pin",
305 | )
306 | ] = validate_pin(CONF_TX_PIN, device)
307 | if require_rx:
308 | hub_schema[
309 | cv.Required(
310 | CONF_RX_PIN,
311 | msg=f"Component {name} requires this uart bus to declare a rx_pin",
312 | )
313 | ] = validate_pin(CONF_RX_PIN, device)
314 | if require_cts:
315 | hub_schema[
316 | cv.Required(
317 | CONF_CTS_PIN,
318 | msg=f"Component {name} requires this uart bus to declare a cts_pin",
319 | )
320 | ] = validate_pin(CONF_CTS_PIN, device)
321 | if require_rts:
322 | hub_schema[
323 | cv.Required(
324 | CONF_RTS_PIN,
325 | msg=f"Component {name} requires this uart bus to declare a rts_pin",
326 | )
327 | ] = validate_pin(CONF_RTS_PIN, device)
328 | if baud_rate is not None:
329 | hub_schema[cv.Required(CONF_BAUD_RATE)] = validate_baud_rate
330 | return cv.Schema(hub_schema, extra=cv.ALLOW_EXTRA)(hub_config)
331 |
332 | return cv.Schema(
333 | {cv.Required(CONF_UART_ID): fv.id_declaration_match_schema(validate_hub)},
334 | extra=cv.ALLOW_EXTRA,
335 | )
336 |
337 |
338 | async def register_uart_device(var, config):
339 | """Register a UART device, setting up all the internal values.
340 |
341 | This is a coroutine, you need to await it with a 'yield' expression!
342 | """
343 | parent = await cg.get_variable(config[CONF_UART_ID])
344 | cg.add(var.set_uart_parent(parent))
345 |
346 |
347 | @automation.register_action(
348 | "uart.write",
349 | UARTWriteAction,
350 | cv.maybe_simple_value(
351 | {
352 | cv.GenerateID(): cv.use_id(UARTComponent),
353 | cv.Required(CONF_DATA): cv.templatable(validate_raw_data),
354 | },
355 | key=CONF_DATA,
356 | ),
357 | )
358 | async def uart_write_to_code(config, action_id, template_arg, args):
359 | var = cg.new_Pvariable(action_id, template_arg)
360 | await cg.register_parented(var, config[CONF_ID])
361 | data = config[CONF_DATA]
362 | if isinstance(data, bytes):
363 | data = list(data)
364 |
365 | if cg.is_template(data):
366 | templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8))
367 | cg.add(var.set_data_template(templ))
368 | else:
369 | cg.add(var.set_data_static(data))
370 | return var
371 |
--------------------------------------------------------------------------------
/components/uart/automation.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "uart.h"
4 | #include "esphome/core/automation.h"
5 |
6 | namespace esphome {
7 | namespace uart {
8 |
9 | template class UARTWriteAction : public Action, public Parented {
10 | public:
11 | void set_data_template(std::function(Ts...)> func) {
12 | this->data_func_ = func;
13 | this->static_ = false;
14 | }
15 | void set_data_static(const std::vector &data) {
16 | this->data_static_ = data;
17 | this->static_ = true;
18 | }
19 |
20 | void play(Ts... x) override {
21 | if (this->static_) {
22 | this->parent_->write_array(this->data_static_);
23 | } else {
24 | auto val = this->data_func_(x...);
25 | this->parent_->write_array(val);
26 | }
27 | }
28 |
29 | protected:
30 | bool static_{false};
31 | std::function(Ts...)> data_func_{};
32 | std::vector data_static_{};
33 | };
34 |
35 | } // namespace uart
36 | } // namespace esphome
37 |
--------------------------------------------------------------------------------
/components/uart/switch/__init__.py:
--------------------------------------------------------------------------------
1 | import esphome.codegen as cg
2 | import esphome.config_validation as cv
3 | from esphome.components import switch, uart
4 | from esphome.const import CONF_DATA, CONF_ID, CONF_INVERTED, CONF_SEND_EVERY
5 | from esphome.core import HexInt
6 | from .. import uart_ns, validate_raw_data
7 |
8 | DEPENDENCIES = ["uart"]
9 |
10 | UARTSwitch = uart_ns.class_("UARTSwitch", switch.Switch, uart.UARTDevice, cg.Component)
11 |
12 |
13 | CONFIG_SCHEMA = (
14 | switch.SWITCH_SCHEMA.extend(
15 | {
16 | cv.GenerateID(): cv.declare_id(UARTSwitch),
17 | cv.Required(CONF_DATA): validate_raw_data,
18 | cv.Optional(CONF_INVERTED): cv.invalid(
19 | "UART switches do not support inverted mode!"
20 | ),
21 | cv.Optional(CONF_SEND_EVERY): cv.positive_time_period_milliseconds,
22 | }
23 | )
24 | .extend(uart.UART_DEVICE_SCHEMA)
25 | .extend(cv.COMPONENT_SCHEMA)
26 | )
27 |
28 |
29 | async def to_code(config):
30 | var = cg.new_Pvariable(config[CONF_ID])
31 | await cg.register_component(var, config)
32 | await switch.register_switch(var, config)
33 | await uart.register_uart_device(var, config)
34 |
35 | data = config[CONF_DATA]
36 | if isinstance(data, bytes):
37 | data = [HexInt(x) for x in data]
38 | cg.add(var.set_data(data))
39 |
40 | if CONF_SEND_EVERY in config:
41 | cg.add(var.set_send_every(config[CONF_SEND_EVERY]))
42 |
--------------------------------------------------------------------------------
/components/uart/switch/uart_switch.cpp:
--------------------------------------------------------------------------------
1 | #include "uart_switch.h"
2 | #include "esphome/core/log.h"
3 |
4 | namespace esphome {
5 | namespace uart {
6 |
7 | static const char *const TAG = "uart.switch";
8 |
9 | void UARTSwitch::loop() {
10 | if (this->state && this->send_every_) {
11 | const uint32_t now = millis();
12 | if (now - this->last_transmission_ > this->send_every_) {
13 | this->write_command_();
14 | this->last_transmission_ = now;
15 | }
16 | }
17 | }
18 |
19 | void UARTSwitch::write_command_() {
20 | ESP_LOGD(TAG, "'%s': Sending data...", this->get_name().c_str());
21 | this->write_array(this->data_.data(), this->data_.size());
22 | }
23 |
24 | void UARTSwitch::write_state(bool state) {
25 | if (!state) {
26 | this->publish_state(false);
27 | return;
28 | }
29 |
30 | this->publish_state(true);
31 | this->write_command_();
32 |
33 | if (this->send_every_ == 0) {
34 | this->publish_state(false);
35 | } else {
36 | this->last_transmission_ = millis();
37 | }
38 | }
39 | void UARTSwitch::dump_config() {
40 | LOG_SWITCH("", "UART Switch", this);
41 | if (this->send_every_) {
42 | ESP_LOGCONFIG(TAG, " Send Every: %u", this->send_every_);
43 | }
44 | }
45 |
46 | } // namespace uart
47 | } // namespace esphome
48 |
--------------------------------------------------------------------------------
/components/uart/switch/uart_switch.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "esphome/core/component.h"
4 | #include "esphome/components/uart/uart.h"
5 | #include "esphome/components/switch/switch.h"
6 |
7 | namespace esphome {
8 | namespace uart {
9 |
10 | class UARTSwitch : public switch_::Switch, public UARTDevice, public Component {
11 | public:
12 | void loop() override;
13 |
14 | void set_data(const std::vector &data) { data_ = data; }
15 | void set_send_every(uint32_t send_every) { this->send_every_ = send_every; }
16 |
17 | void dump_config() override;
18 |
19 | protected:
20 | void write_command_();
21 | void write_state(bool state) override;
22 | std::vector data_;
23 | uint32_t send_every_;
24 | uint32_t last_transmission_;
25 | };
26 |
27 | } // namespace uart
28 | } // namespace esphome
29 |
--------------------------------------------------------------------------------
/components/uart/uart.cpp:
--------------------------------------------------------------------------------
1 | #include "uart.h"
2 | #include "esphome/core/log.h"
3 | #include "esphome/core/helpers.h"
4 | #include "esphome/core/application.h"
5 | #include "esphome/core/defines.h"
6 |
7 | namespace esphome {
8 | namespace uart {
9 |
10 | static const char *const TAG = "uart";
11 |
12 | void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits, UARTParityOptions parity,
13 | uint8_t data_bits, UARTHardwareFlowControl hw_flowctrl) {
14 | if (this->parent_->get_baud_rate() != baud_rate) {
15 | ESP_LOGE(TAG, " Invalid baud_rate: Integration requested baud_rate %u but you have %u!", baud_rate,
16 | this->parent_->get_baud_rate());
17 | }
18 | if (this->parent_->get_stop_bits() != stop_bits) {
19 | ESP_LOGE(TAG, " Invalid stop bits: Integration requested stop_bits %u but you have %u!", stop_bits,
20 | this->parent_->get_stop_bits());
21 | }
22 | if (this->parent_->get_data_bits() != data_bits) {
23 | ESP_LOGE(TAG, " Invalid number of data bits: Integration requested %u data bits but you have %u!", data_bits,
24 | this->parent_->get_data_bits());
25 | }
26 | if (this->parent_->get_parity() != parity) {
27 | ESP_LOGE(TAG, " Invalid parity: Integration requested parity %s but you have %s!",
28 | LOG_STR_ARG(parity_to_str(parity)), LOG_STR_ARG(parity_to_str(this->parent_->get_parity())));
29 | }
30 | if (this->parent_->get_hw_flowctrl() != hw_flowctrl) {
31 | ESP_LOGE(TAG, " Invalid hw_flowctrl: Integration requested hw_flowctrl %s but you have %s!",
32 | LOG_STR_ARG(hw_flowctrl_to_str(hw_flowctrl)), LOG_STR_ARG(hw_flowctrl_to_str(this->parent_->get_hw_flowctrl())));
33 | }
34 | }
35 |
36 | const LogString *parity_to_str(UARTParityOptions parity) {
37 | switch (parity) {
38 | case UART_CONFIG_PARITY_NONE:
39 | return LOG_STR("NONE");
40 | case UART_CONFIG_PARITY_EVEN:
41 | return LOG_STR("EVEN");
42 | case UART_CONFIG_PARITY_ODD:
43 | return LOG_STR("ODD");
44 | default:
45 | return LOG_STR("UNKNOWN");
46 | }
47 | }
48 |
49 | const LogString *hw_flowctrl_to_str(UARTHardwareFlowControl hw_flowctrl) {
50 | switch (hw_flowctrl) {
51 | case UART_CONFIG_HW_FLOWCTRL_DISABLE:
52 | return LOG_STR("DISABLE");
53 | case UART_CONFIG_HW_FLOWCTRL_CTS:
54 | return LOG_STR("CTS");
55 | case UART_CONFIG_HW_FLOWCTRL_RTS:
56 | return LOG_STR("RTS");
57 | case UART_CONFIG_HW_FLOWCTRL_CTS_RTS:
58 | return LOG_STR("CTS_RTS");
59 | case UART_CONFIG_HW_FLOWCTRL_MAX:
60 | return LOG_STR("MAX");
61 | default:
62 | return LOG_STR("UNKNOWN");
63 | }
64 | }
65 |
66 | } // namespace uart
67 | } // namespace esphome
68 |
--------------------------------------------------------------------------------
/components/uart/uart.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include "esphome/core/component.h"
5 | #include "esphome/core/hal.h"
6 | #include "esphome/core/log.h"
7 | #include "uart_component.h"
8 |
9 | namespace esphome {
10 | namespace uart {
11 |
12 | class UARTDevice {
13 | public:
14 | UARTDevice() = default;
15 | UARTDevice(UARTComponent *parent) : parent_(parent) {}
16 |
17 | void set_uart_parent(UARTComponent *parent) { this->parent_ = parent; }
18 |
19 | void write_byte(uint8_t data) { this->parent_->write_byte(data); }
20 |
21 | void write_array(const uint8_t *data, size_t len) { this->parent_->write_array(data, len); }
22 | void write_array(const std::vector &data) { this->parent_->write_array(data); }
23 | template void write_array(const std::array &data) {
24 | this->parent_->write_array(data.data(), data.size());
25 | }
26 |
27 | void write_str(const char *str) { this->parent_->write_str(str); }
28 |
29 | bool read_byte(uint8_t *data) { return this->parent_->read_byte(data); }
30 | bool peek_byte(uint8_t *data) { return this->parent_->peek_byte(data); }
31 |
32 | bool read_array(uint8_t *data, size_t len) { return this->parent_->read_array(data, len); }
33 | template optional> read_array() { // NOLINT
34 | std::array res;
35 | if (!this->read_array(res.data(), N)) {
36 | return {};
37 | }
38 | return res;
39 | }
40 |
41 | int available() { return this->parent_->available(); }
42 |
43 | void flush() { return this->parent_->flush(); }
44 |
45 | // Compat APIs
46 | int read() {
47 | uint8_t data;
48 | if (!this->read_byte(&data))
49 | return -1;
50 | return data;
51 | }
52 | size_t write(uint8_t data) {
53 | this->write_byte(data);
54 | return 1;
55 | }
56 | int peek() {
57 | uint8_t data;
58 | if (!this->peek_byte(&data))
59 | return -1;
60 | return data;
61 | }
62 |
63 | /// Check that the configuration of the UART bus matches the provided values and otherwise print a warning
64 | void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits = 1,
65 | UARTParityOptions parity = UART_CONFIG_PARITY_NONE, uint8_t data_bits = 8,
66 | UARTHardwareFlowControl hw_flowctrl = UART_CONFIG_HW_FLOWCTRL_DISABLE);
67 |
68 | protected:
69 | UARTComponent *parent_{nullptr};
70 | };
71 |
72 | } // namespace uart
73 | } // namespace esphome
74 |
--------------------------------------------------------------------------------
/components/uart/uart_component.cpp:
--------------------------------------------------------------------------------
1 | #include "uart_component.h"
2 |
3 | namespace esphome {
4 | namespace uart {
5 |
6 | static const char *const TAG = "uart";
7 |
8 | bool UARTComponent::check_read_timeout_(size_t len) {
9 | if (this->available() >= int(len))
10 | return true;
11 |
12 | uint32_t start_time = millis();
13 | while (this->available() < int(len)) {
14 | if (millis() - start_time > 100) {
15 | ESP_LOGE(TAG, "Reading from UART timed out at byte %u!", this->available());
16 | return false;
17 | }
18 | yield();
19 | }
20 | return true;
21 | }
22 |
23 | } // namespace uart
24 | } // namespace esphome
25 |
--------------------------------------------------------------------------------
/components/uart/uart_component.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include "esphome/core/defines.h"
6 | #include "esphome/core/component.h"
7 | #include "esphome/core/hal.h"
8 | #include "esphome/core/log.h"
9 | #ifdef USE_UART_DEBUGGER
10 | #include "esphome/core/automation.h"
11 | #endif
12 |
13 | namespace esphome {
14 | namespace uart {
15 |
16 | enum UARTParityOptions {
17 | UART_CONFIG_PARITY_NONE,
18 | UART_CONFIG_PARITY_EVEN,
19 | UART_CONFIG_PARITY_ODD,
20 | };
21 |
22 | #ifdef USE_UART_DEBUGGER
23 | enum UARTDirection {
24 | UART_DIRECTION_RX,
25 | UART_DIRECTION_TX,
26 | UART_DIRECTION_BOTH,
27 | };
28 | #endif
29 |
30 | const LogString *parity_to_str(UARTParityOptions parity);
31 |
32 | enum UARTHardwareFlowControl {
33 | UART_CONFIG_HW_FLOWCTRL_DISABLE,
34 | UART_CONFIG_HW_FLOWCTRL_RTS,
35 | UART_CONFIG_HW_FLOWCTRL_CTS,
36 | UART_CONFIG_HW_FLOWCTRL_CTS_RTS,
37 | UART_CONFIG_HW_FLOWCTRL_MAX
38 | };
39 |
40 | const LogString *hw_flowctrl_to_str(UARTHardwareFlowControl hw_flowctrl);
41 |
42 | class UARTComponent {
43 | public:
44 | void write_array(const std::vector &data) { this->write_array(&data[0], data.size()); }
45 | void write_byte(uint8_t data) { this->write_array(&data, 1); };
46 | void write_str(const char *str) {
47 | const auto *data = reinterpret_cast(str);
48 | this->write_array(data, strlen(str));
49 | };
50 |
51 | virtual void write_array(const uint8_t *data, size_t len) = 0;
52 |
53 | bool read_byte(uint8_t *data) { return this->read_array(data, 1); };
54 | virtual bool peek_byte(uint8_t *data) = 0;
55 | virtual bool read_array(uint8_t *data, size_t len) = 0;
56 |
57 | /// Return available number of bytes.
58 | virtual int available() = 0;
59 | /// Block until all bytes have been written to the UART bus.
60 | virtual void flush() = 0;
61 |
62 | void set_tx_pin(InternalGPIOPin *tx_pin) { this->tx_pin_ = tx_pin; }
63 | void set_rx_pin(InternalGPIOPin *rx_pin) { this->rx_pin_ = rx_pin; }
64 | void set_cts_pin(InternalGPIOPin *cts_pin) { this->cts_pin_ = cts_pin; }
65 | void set_rts_pin(InternalGPIOPin *rts_pin) { this->rts_pin_ = rts_pin; }
66 | void set_rx_buffer_size(size_t rx_buffer_size) { this->rx_buffer_size_ = rx_buffer_size; }
67 | size_t get_rx_buffer_size() { return this->rx_buffer_size_; }
68 |
69 | void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; }
70 | uint8_t get_stop_bits() const { return this->stop_bits_; }
71 | void set_data_bits(uint8_t data_bits) { this->data_bits_ = data_bits; }
72 | uint8_t get_data_bits() const { return this->data_bits_; }
73 | void set_parity(UARTParityOptions parity) { this->parity_ = parity; }
74 | UARTParityOptions get_parity() const { return this->parity_; }
75 | void set_hw_flowctrl(UARTHardwareFlowControl hw_flowctrl) { this->hw_flowctrl_ = hw_flowctrl; }
76 | UARTHardwareFlowControl get_hw_flowctrl() const { return this->hw_flowctrl_; }
77 | void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; }
78 | uint32_t get_baud_rate() const { return baud_rate_; }
79 |
80 | #ifdef USE_UART_DEBUGGER
81 | void add_debug_callback(std::function &&callback) {
82 | this->debug_callback_.add(std::move(callback));
83 | }
84 | #endif
85 |
86 | protected:
87 | virtual void check_logger_conflict() = 0;
88 | bool check_read_timeout_(size_t len = 1);
89 |
90 | InternalGPIOPin *tx_pin_;
91 | InternalGPIOPin *rx_pin_;
92 | InternalGPIOPin *cts_pin_;
93 | InternalGPIOPin *rts_pin_;
94 | size_t rx_buffer_size_;
95 | uint32_t baud_rate_;
96 | uint8_t stop_bits_;
97 | uint8_t data_bits_;
98 | UARTParityOptions parity_;
99 | UARTHardwareFlowControl hw_flowctrl_;
100 | #ifdef USE_UART_DEBUGGER
101 | CallbackManager debug_callback_{};
102 | #endif
103 | };
104 |
105 | } // namespace uart
106 | } // namespace esphome
107 |
--------------------------------------------------------------------------------
/components/uart/uart_component_esp32_arduino.cpp:
--------------------------------------------------------------------------------
1 | #ifdef USE_ESP32_FRAMEWORK_ARDUINO
2 | #include "esphome/core/application.h"
3 | #include "esphome/core/defines.h"
4 | #include "esphome/core/helpers.h"
5 | #include "esphome/core/log.h"
6 | #include "uart_component_esp32_arduino.h"
7 |
8 | #ifdef USE_LOGGER
9 | #include "esphome/components/logger/logger.h"
10 | #endif
11 |
12 | namespace esphome {
13 | namespace uart {
14 | static const char *const TAG = "uart.arduino_esp32";
15 |
16 | static const uint32_t UART_PARITY_EVEN = 0 << 0;
17 | static const uint32_t UART_PARITY_ODD = 1 << 0;
18 | static const uint32_t UART_PARITY_ENABLE = 1 << 1;
19 | static const uint32_t UART_NB_BIT_5 = 0 << 2;
20 | static const uint32_t UART_NB_BIT_6 = 1 << 2;
21 | static const uint32_t UART_NB_BIT_7 = 2 << 2;
22 | static const uint32_t UART_NB_BIT_8 = 3 << 2;
23 | static const uint32_t UART_NB_STOP_BIT_1 = 1 << 4;
24 | static const uint32_t UART_NB_STOP_BIT_2 = 3 << 4;
25 | static const uint32_t UART_TICK_APB_CLOCK = 1 << 27;
26 |
27 | uint32_t ESP32ArduinoUARTComponent::get_config() {
28 | uint32_t config = 0;
29 |
30 | /*
31 | * All bits numbers below come from
32 | * framework-arduinoespressif32/cores/esp32/esp32-hal-uart.h
33 | * And more specifically conf0 union in uart_dev_t.
34 | *
35 | * Below is bit used from conf0 union.
36 | * :
37 | * parity:0 0:even 1:odd
38 | * parity_en:1 Set this bit to enable uart parity check.
39 | * bit_num:2-4 0:5bits 1:6bits 2:7bits 3:8bits
40 | * stop_bit_num:4-6 stop bit. 1:1bit 2:1.5bits 3:2bits
41 | * tick_ref_always_on:27 select the clock.1:apb clock:ref_tick
42 | */
43 |
44 | if (this->parity_ == UART_CONFIG_PARITY_EVEN)
45 | config |= UART_PARITY_EVEN | UART_PARITY_ENABLE;
46 | else if (this->parity_ == UART_CONFIG_PARITY_ODD)
47 | config |= UART_PARITY_ODD | UART_PARITY_ENABLE;
48 |
49 | switch (this->data_bits_) {
50 | case 5:
51 | config |= UART_NB_BIT_5;
52 | break;
53 | case 6:
54 | config |= UART_NB_BIT_6;
55 | break;
56 | case 7:
57 | config |= UART_NB_BIT_7;
58 | break;
59 | case 8:
60 | config |= UART_NB_BIT_8;
61 | break;
62 | }
63 |
64 | if (this->stop_bits_ == 1)
65 | config |= UART_NB_STOP_BIT_1;
66 | else
67 | config |= UART_NB_STOP_BIT_2;
68 |
69 | config |= UART_TICK_APB_CLOCK;
70 |
71 | return config;
72 | }
73 |
74 | void ESP32ArduinoUARTComponent::setup() {
75 | ESP_LOGCONFIG(TAG, "Setting up UART...");
76 | // Use Arduino HardwareSerial UARTs if all used pins match the ones
77 | // preconfigured by the platform. For example if RX disabled but TX pin
78 | // is 1 we still want to use Serial.
79 | bool is_default_tx, is_default_rx;
80 | #ifdef CONFIG_IDF_TARGET_ESP32C3
81 | is_default_tx = tx_pin_ == nullptr || tx_pin_->get_pin() == 21;
82 | is_default_rx = rx_pin_ == nullptr || rx_pin_->get_pin() == 20;
83 | #else
84 | is_default_tx = tx_pin_ == nullptr || tx_pin_->get_pin() == 1;
85 | is_default_rx = rx_pin_ == nullptr || rx_pin_->get_pin() == 3;
86 | #endif
87 | if (is_default_tx && is_default_rx) {
88 | this->hw_serial_ = &Serial;
89 | } else {
90 | static uint8_t next_uart_num = 1;
91 | this->hw_serial_ = new HardwareSerial(next_uart_num++); // NOLINT(cppcoreguidelines-owning-memory)
92 | }
93 | int8_t tx = this->tx_pin_ != nullptr ? this->tx_pin_->get_pin() : -1;
94 | int8_t rx = this->rx_pin_ != nullptr ? this->rx_pin_->get_pin() : -1;
95 | bool invert = false;
96 | if (tx_pin_ != nullptr && tx_pin_->is_inverted())
97 | invert = true;
98 | if (rx_pin_ != nullptr && rx_pin_->is_inverted())
99 | invert = true;
100 | this->hw_serial_->begin(this->baud_rate_, get_config(), rx, tx, invert);
101 | this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
102 | }
103 |
104 | void ESP32ArduinoUARTComponent::dump_config() {
105 | ESP_LOGCONFIG(TAG, "UART Bus:");
106 | LOG_PIN(" TX Pin: ", tx_pin_);
107 | LOG_PIN(" RX Pin: ", rx_pin_);
108 | if (this->rx_pin_ != nullptr) {
109 | ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_);
110 | }
111 | ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
112 | ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_);
113 | ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_)));
114 | ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
115 | this->check_logger_conflict();
116 | }
117 |
118 | void ESP32ArduinoUARTComponent::write_array(const uint8_t *data, size_t len) {
119 | this->hw_serial_->write(data, len);
120 | #ifdef USE_UART_DEBUGGER
121 | for (size_t i = 0; i < len; i++) {
122 | this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
123 | }
124 | #endif
125 | }
126 |
127 | bool ESP32ArduinoUARTComponent::peek_byte(uint8_t *data) {
128 | if (!this->check_read_timeout_())
129 | return false;
130 | *data = this->hw_serial_->peek();
131 | return true;
132 | }
133 |
134 | bool ESP32ArduinoUARTComponent::read_array(uint8_t *data, size_t len) {
135 | if (!this->check_read_timeout_(len))
136 | return false;
137 | this->hw_serial_->readBytes(data, len);
138 | #ifdef USE_UART_DEBUGGER
139 | for (size_t i = 0; i < len; i++) {
140 | this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
141 | }
142 | #endif
143 | return true;
144 | }
145 |
146 | int ESP32ArduinoUARTComponent::available() { return this->hw_serial_->available(); }
147 | void ESP32ArduinoUARTComponent::flush() {
148 | ESP_LOGVV(TAG, " Flushing...");
149 | this->hw_serial_->flush();
150 | }
151 |
152 | void ESP32ArduinoUARTComponent::check_logger_conflict() {
153 | #ifdef USE_LOGGER
154 | if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) {
155 | return;
156 | }
157 |
158 | if (this->hw_serial_ == logger::global_logger->get_hw_serial()) {
159 | ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please "
160 | "disable logging over the serial port by setting logger->baud_rate to 0.");
161 | }
162 | #endif
163 | }
164 |
165 | } // namespace uart
166 | } // namespace esphome
167 | #endif // USE_ESP32_FRAMEWORK_ARDUINO
168 |
--------------------------------------------------------------------------------
/components/uart/uart_component_esp32_arduino.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifdef USE_ESP32_FRAMEWORK_ARDUINO
4 |
5 | #include
6 | #include
7 | #include "esphome/core/component.h"
8 | #include "esphome/core/hal.h"
9 | #include "esphome/core/log.h"
10 | #include "uart_component.h"
11 |
12 | namespace esphome {
13 | namespace uart {
14 |
15 | class ESP32ArduinoUARTComponent : public UARTComponent, public Component {
16 | public:
17 | void setup() override;
18 | void dump_config() override;
19 | float get_setup_priority() const override { return setup_priority::BUS; }
20 |
21 | void write_array(const uint8_t *data, size_t len) override;
22 |
23 | bool peek_byte(uint8_t *data) override;
24 | bool read_array(uint8_t *data, size_t len) override;
25 |
26 | int available() override;
27 | void flush() override;
28 |
29 | uint32_t get_config();
30 |
31 | protected:
32 | void check_logger_conflict() override;
33 |
34 | HardwareSerial *hw_serial_{nullptr};
35 | };
36 |
37 | } // namespace uart
38 | } // namespace esphome
39 |
40 | #endif // USE_ESP32_FRAMEWORK_ARDUINO
41 |
--------------------------------------------------------------------------------
/components/uart/uart_component_esp8266.cpp:
--------------------------------------------------------------------------------
1 | #ifdef USE_ESP8266
2 | #include "uart_component_esp8266.h"
3 | #include "esphome/core/application.h"
4 | #include "esphome/core/defines.h"
5 | #include "esphome/core/helpers.h"
6 | #include "esphome/core/log.h"
7 |
8 | #ifdef USE_LOGGER
9 | #include "esphome/components/logger/logger.h"
10 | #endif
11 |
12 | namespace esphome {
13 | namespace uart {
14 |
15 | static const char *const TAG = "uart.arduino_esp8266";
16 | bool ESP8266UartComponent::serial0_in_use = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
17 |
18 | uint32_t ESP8266UartComponent::get_config() {
19 | uint32_t config = 0;
20 |
21 | if (this->parity_ == UART_CONFIG_PARITY_NONE)
22 | config |= UART_PARITY_NONE;
23 | else if (this->parity_ == UART_CONFIG_PARITY_EVEN)
24 | config |= UART_PARITY_EVEN;
25 | else if (this->parity_ == UART_CONFIG_PARITY_ODD)
26 | config |= UART_PARITY_ODD;
27 |
28 | switch (this->data_bits_) {
29 | case 5:
30 | config |= UART_NB_BIT_5;
31 | break;
32 | case 6:
33 | config |= UART_NB_BIT_6;
34 | break;
35 | case 7:
36 | config |= UART_NB_BIT_7;
37 | break;
38 | case 8:
39 | config |= UART_NB_BIT_8;
40 | break;
41 | }
42 |
43 | if (this->stop_bits_ == 1)
44 | config |= UART_NB_STOP_BIT_1;
45 | else
46 | config |= UART_NB_STOP_BIT_2;
47 |
48 | if (this->tx_pin_ != nullptr && this->tx_pin_->is_inverted())
49 | config |= BIT(22);
50 | if (this->rx_pin_ != nullptr && this->rx_pin_->is_inverted())
51 | config |= BIT(19);
52 |
53 | return config;
54 | }
55 |
56 | void ESP8266UartComponent::setup() {
57 | ESP_LOGCONFIG(TAG, "Setting up UART bus...");
58 | // Use Arduino HardwareSerial UARTs if all used pins match the ones
59 | // preconfigured by the platform. For example if RX disabled but TX pin
60 | // is 1 we still want to use Serial.
61 | SerialConfig config = static_cast(get_config());
62 |
63 | if (!ESP8266UartComponent::serial0_in_use && (tx_pin_ == nullptr || tx_pin_->get_pin() == 1) &&
64 | (rx_pin_ == nullptr || rx_pin_->get_pin() == 3)
65 | #ifdef USE_LOGGER
66 | // we will use UART0 if logger isn't using it in swapped mode
67 | && (logger::global_logger->get_hw_serial() == nullptr ||
68 | logger::global_logger->get_uart() != logger::UART_SELECTION_UART0_SWAP)
69 | #endif
70 | ) {
71 | this->hw_serial_ = &Serial;
72 | this->hw_serial_->begin(this->baud_rate_, config);
73 | this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
74 | ESP8266UartComponent::serial0_in_use = true;
75 | } else if (!ESP8266UartComponent::serial0_in_use && (tx_pin_ == nullptr || tx_pin_->get_pin() == 15) &&
76 | (rx_pin_ == nullptr || rx_pin_->get_pin() == 13)
77 | #ifdef USE_LOGGER
78 | // we will use UART0 swapped if logger isn't using it in regular mode
79 | && (logger::global_logger->get_hw_serial() == nullptr ||
80 | logger::global_logger->get_uart() != logger::UART_SELECTION_UART0)
81 | #endif
82 | ) {
83 | this->hw_serial_ = &Serial;
84 | this->hw_serial_->begin(this->baud_rate_, config);
85 | this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
86 | this->hw_serial_->swap();
87 | ESP8266UartComponent::serial0_in_use = true;
88 | } else if ((tx_pin_ == nullptr || tx_pin_->get_pin() == 2) && (rx_pin_ == nullptr || rx_pin_->get_pin() == 8)) {
89 | this->hw_serial_ = &Serial1;
90 | this->hw_serial_->begin(this->baud_rate_, config);
91 | this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
92 | } else {
93 | this->sw_serial_ = new ESP8266SoftwareSerial(); // NOLINT
94 | this->sw_serial_->setup(tx_pin_, rx_pin_, this->baud_rate_, this->stop_bits_, this->data_bits_, this->parity_,
95 | this->rx_buffer_size_);
96 | }
97 | }
98 |
99 | void ESP8266UartComponent::dump_config() {
100 | ESP_LOGCONFIG(TAG, "UART Bus:");
101 | LOG_PIN(" TX Pin: ", tx_pin_);
102 | LOG_PIN(" RX Pin: ", rx_pin_);
103 | if (this->rx_pin_ != nullptr) {
104 | ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); // NOLINT
105 | }
106 | ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
107 | ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_);
108 | ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_)));
109 | ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
110 | if (this->hw_serial_ != nullptr) {
111 | ESP_LOGCONFIG(TAG, " Using hardware serial interface.");
112 | } else {
113 | ESP_LOGCONFIG(TAG, " Using software serial");
114 | }
115 | this->check_logger_conflict();
116 | }
117 |
118 | void ESP8266UartComponent::check_logger_conflict() {
119 | #ifdef USE_LOGGER
120 | if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) {
121 | return;
122 | }
123 |
124 | if (this->hw_serial_ == logger::global_logger->get_hw_serial()) {
125 | ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please "
126 | "disable logging over the serial port by setting logger->baud_rate to 0.");
127 | }
128 | #endif
129 | }
130 |
131 | void ESP8266UartComponent::write_array(const uint8_t *data, size_t len) {
132 | if (this->hw_serial_ != nullptr) {
133 | this->hw_serial_->write(data, len);
134 | } else {
135 | for (size_t i = 0; i < len; i++)
136 | this->sw_serial_->write_byte(data[i]);
137 | }
138 | #ifdef USE_UART_DEBUGGER
139 | for (size_t i = 0; i < len; i++) {
140 | this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
141 | }
142 | #endif
143 | }
144 | bool ESP8266UartComponent::peek_byte(uint8_t *data) {
145 | if (!this->check_read_timeout_())
146 | return false;
147 | if (this->hw_serial_ != nullptr) {
148 | *data = this->hw_serial_->peek();
149 | } else {
150 | *data = this->sw_serial_->peek_byte();
151 | }
152 | return true;
153 | }
154 | bool ESP8266UartComponent::read_array(uint8_t *data, size_t len) {
155 | if (!this->check_read_timeout_(len))
156 | return false;
157 | if (this->hw_serial_ != nullptr) {
158 | this->hw_serial_->readBytes(data, len);
159 | } else {
160 | for (size_t i = 0; i < len; i++)
161 | data[i] = this->sw_serial_->read_byte();
162 | }
163 | #ifdef USE_UART_DEBUGGER
164 | for (size_t i = 0; i < len; i++) {
165 | this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
166 | }
167 | #endif
168 | return true;
169 | }
170 | int ESP8266UartComponent::available() {
171 | if (this->hw_serial_ != nullptr) {
172 | return this->hw_serial_->available();
173 | } else {
174 | return this->sw_serial_->available();
175 | }
176 | }
177 | void ESP8266UartComponent::flush() {
178 | ESP_LOGVV(TAG, " Flushing...");
179 | if (this->hw_serial_ != nullptr) {
180 | this->hw_serial_->flush();
181 | } else {
182 | this->sw_serial_->flush();
183 | }
184 | }
185 | void ESP8266SoftwareSerial::setup(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, uint32_t baud_rate,
186 | uint8_t stop_bits, uint32_t data_bits, UARTParityOptions parity,
187 | size_t rx_buffer_size) {
188 | this->bit_time_ = F_CPU / baud_rate;
189 | this->rx_buffer_size_ = rx_buffer_size;
190 | this->stop_bits_ = stop_bits;
191 | this->data_bits_ = data_bits;
192 | this->parity_ = parity;
193 | if (tx_pin != nullptr) {
194 | gpio_tx_pin_ = tx_pin;
195 | gpio_tx_pin_->setup();
196 | tx_pin_ = gpio_tx_pin_->to_isr();
197 | tx_pin_.digital_write(true);
198 | }
199 | if (rx_pin != nullptr) {
200 | gpio_rx_pin_ = rx_pin;
201 | gpio_rx_pin_->setup();
202 | rx_pin_ = gpio_rx_pin_->to_isr();
203 | rx_buffer_ = new uint8_t[this->rx_buffer_size_]; // NOLINT
204 | gpio_rx_pin_->attach_interrupt(ESP8266SoftwareSerial::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE);
205 | }
206 | }
207 | void IRAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg) {
208 | uint32_t wait = arg->bit_time_ + arg->bit_time_ / 3 - 500;
209 | const uint32_t start = arch_get_cpu_cycle_count();
210 | uint8_t rec = 0;
211 | // Manually unroll the loop
212 | for (int i = 0; i < arg->data_bits_; i++)
213 | rec |= arg->read_bit_(&wait, start) << i;
214 |
215 | /* If parity is enabled, just read it and ignore it. */
216 | /* TODO: Should we check parity? Or is it too slow for nothing added..*/
217 | if (arg->parity_ == UART_CONFIG_PARITY_EVEN || arg->parity_ == UART_CONFIG_PARITY_ODD)
218 | arg->read_bit_(&wait, start);
219 |
220 | // Stop bit
221 | arg->wait_(&wait, start);
222 | if (arg->stop_bits_ == 2)
223 | arg->wait_(&wait, start);
224 |
225 | arg->rx_buffer_[arg->rx_in_pos_] = rec;
226 | arg->rx_in_pos_ = (arg->rx_in_pos_ + 1) % arg->rx_buffer_size_;
227 | // Clear RX pin so that the interrupt doesn't re-trigger right away again.
228 | arg->rx_pin_.clear_interrupt();
229 | }
230 | void IRAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) {
231 | if (this->gpio_tx_pin_ == nullptr) {
232 | ESP_LOGE(TAG, "UART doesn't have TX pins set!");
233 | return;
234 | }
235 | bool parity_bit = false;
236 | bool need_parity_bit = true;
237 | if (this->parity_ == UART_CONFIG_PARITY_EVEN)
238 | parity_bit = false;
239 | else if (this->parity_ == UART_CONFIG_PARITY_ODD)
240 | parity_bit = true;
241 | else
242 | need_parity_bit = false;
243 |
244 | {
245 | InterruptLock lock;
246 | uint32_t wait = this->bit_time_;
247 | const uint32_t start = arch_get_cpu_cycle_count();
248 | // Start bit
249 | this->write_bit_(false, &wait, start);
250 | for (int i = 0; i < this->data_bits_; i++) {
251 | bool bit = data & (1 << i);
252 | this->write_bit_(bit, &wait, start);
253 | if (need_parity_bit)
254 | parity_bit ^= bit;
255 | }
256 | if (need_parity_bit)
257 | this->write_bit_(parity_bit, &wait, start);
258 | // Stop bit
259 | this->write_bit_(true, &wait, start);
260 | if (this->stop_bits_ == 2)
261 | this->wait_(&wait, start);
262 | }
263 | }
264 | void IRAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) {
265 | while (arch_get_cpu_cycle_count() - start < *wait)
266 | ;
267 | *wait += this->bit_time_;
268 | }
269 | bool IRAM_ATTR ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint32_t &start) {
270 | this->wait_(wait, start);
271 | return this->rx_pin_.digital_read();
272 | }
273 | void IRAM_ATTR ESP8266SoftwareSerial::write_bit_(bool bit, uint32_t *wait, const uint32_t &start) {
274 | this->tx_pin_.digital_write(bit);
275 | this->wait_(wait, start);
276 | }
277 | uint8_t ESP8266SoftwareSerial::read_byte() {
278 | if (this->rx_in_pos_ == this->rx_out_pos_)
279 | return 0;
280 | uint8_t data = this->rx_buffer_[this->rx_out_pos_];
281 | this->rx_out_pos_ = (this->rx_out_pos_ + 1) % this->rx_buffer_size_;
282 | return data;
283 | }
284 | uint8_t ESP8266SoftwareSerial::peek_byte() {
285 | if (this->rx_in_pos_ == this->rx_out_pos_)
286 | return 0;
287 | return this->rx_buffer_[this->rx_out_pos_];
288 | }
289 | void ESP8266SoftwareSerial::flush() {
290 | // Flush is a NO-OP with software serial, all bytes are written immediately.
291 | }
292 | int ESP8266SoftwareSerial::available() {
293 | int avail = int(this->rx_in_pos_) - int(this->rx_out_pos_);
294 | if (avail < 0)
295 | return avail + this->rx_buffer_size_;
296 | return avail;
297 | }
298 |
299 | } // namespace uart
300 | } // namespace esphome
301 | #endif // USE_ESP8266
302 |
--------------------------------------------------------------------------------
/components/uart/uart_component_esp8266.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifdef USE_ESP8266
4 |
5 | #include
6 | #include
7 | #include "esphome/core/component.h"
8 | #include "esphome/core/hal.h"
9 | #include "esphome/core/log.h"
10 | #include "uart_component.h"
11 |
12 | namespace esphome {
13 | namespace uart {
14 |
15 | class ESP8266SoftwareSerial {
16 | public:
17 | void setup(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, uint32_t baud_rate, uint8_t stop_bits,
18 | uint32_t data_bits, UARTParityOptions parity, size_t rx_buffer_size);
19 |
20 | uint8_t read_byte();
21 | uint8_t peek_byte();
22 |
23 | void flush();
24 |
25 | void write_byte(uint8_t data);
26 |
27 | int available();
28 |
29 | protected:
30 | static void gpio_intr(ESP8266SoftwareSerial *arg);
31 |
32 | void wait_(uint32_t *wait, const uint32_t &start);
33 | bool read_bit_(uint32_t *wait, const uint32_t &start);
34 | void write_bit_(bool bit, uint32_t *wait, const uint32_t &start);
35 |
36 | uint32_t bit_time_{0};
37 | uint8_t *rx_buffer_{nullptr};
38 | size_t rx_buffer_size_;
39 | volatile size_t rx_in_pos_{0};
40 | size_t rx_out_pos_{0};
41 | uint8_t stop_bits_;
42 | uint8_t data_bits_;
43 | UARTParityOptions parity_;
44 | InternalGPIOPin *gpio_tx_pin_{nullptr};
45 | ISRInternalGPIOPin tx_pin_;
46 | InternalGPIOPin *gpio_rx_pin_{nullptr};
47 | ISRInternalGPIOPin rx_pin_;
48 | };
49 |
50 | class ESP8266UartComponent : public UARTComponent, public Component {
51 | public:
52 | void setup() override;
53 | void dump_config() override;
54 | float get_setup_priority() const override { return setup_priority::BUS; }
55 |
56 | void write_array(const uint8_t *data, size_t len) override;
57 |
58 | bool peek_byte(uint8_t *data) override;
59 | bool read_array(uint8_t *data, size_t len) override;
60 |
61 | int available() override;
62 | void flush() override;
63 |
64 | uint32_t get_config();
65 |
66 | protected:
67 | void check_logger_conflict() override;
68 |
69 | HardwareSerial *hw_serial_{nullptr};
70 | ESP8266SoftwareSerial *sw_serial_{nullptr};
71 |
72 | private:
73 | static bool serial0_in_use; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
74 | };
75 |
76 | } // namespace uart
77 | } // namespace esphome
78 |
79 | #endif // USE_ESP8266
80 |
--------------------------------------------------------------------------------
/components/uart/uart_component_esp_idf.cpp:
--------------------------------------------------------------------------------
1 | #ifdef USE_ESP_IDF
2 |
3 | #include "uart_component_esp_idf.h"
4 | #include "esphome/core/application.h"
5 | #include "esphome/core/defines.h"
6 | #include "esphome/core/helpers.h"
7 | #include "esphome/core/log.h"
8 |
9 | #ifdef USE_LOGGER
10 | #include "esphome/components/logger/logger.h"
11 | #endif
12 |
13 | namespace esphome {
14 | namespace uart {
15 | static const char *const TAG = "uart.idf";
16 |
17 | uart_config_t IDFUARTComponent::get_config_() {
18 | uart_parity_t parity = UART_PARITY_DISABLE;
19 | if (this->parity_ == UART_CONFIG_PARITY_EVEN)
20 | parity = UART_PARITY_EVEN;
21 | else if (this->parity_ == UART_CONFIG_PARITY_ODD)
22 | parity = UART_PARITY_ODD;
23 |
24 | uart_word_length_t data_bits;
25 | switch (this->data_bits_) {
26 | case 5:
27 | data_bits = UART_DATA_5_BITS;
28 | break;
29 | case 6:
30 | data_bits = UART_DATA_6_BITS;
31 | break;
32 | case 7:
33 | data_bits = UART_DATA_7_BITS;
34 | break;
35 | case 8:
36 | data_bits = UART_DATA_8_BITS;
37 | break;
38 | default:
39 | data_bits = UART_DATA_BITS_MAX;
40 | break;
41 | }
42 |
43 | uart_hw_flowcontrol_t hw_flowctrl;
44 | switch (this->hw_flowctrl_) {
45 | case UART_CONFIG_HW_FLOWCTRL_RTS:
46 | hw_flowctrl = UART_HW_FLOWCTRL_RTS;
47 | break;
48 | case UART_CONFIG_HW_FLOWCTRL_CTS:
49 | hw_flowctrl = UART_HW_FLOWCTRL_CTS;
50 | break;
51 | case UART_CONFIG_HW_FLOWCTRL_CTS_RTS:
52 | hw_flowctrl = UART_HW_FLOWCTRL_CTS_RTS;
53 | break;
54 | case UART_CONFIG_HW_FLOWCTRL_MAX:
55 | hw_flowctrl = UART_HW_FLOWCTRL_MAX;
56 | break;
57 | default:
58 | hw_flowctrl = UART_HW_FLOWCTRL_DISABLE;
59 | break;
60 | }
61 |
62 | uart_config_t uart_config;
63 | uart_config.baud_rate = this->baud_rate_;
64 | uart_config.data_bits = data_bits;
65 | uart_config.parity = parity;
66 | uart_config.stop_bits = this->stop_bits_ == 1 ? UART_STOP_BITS_1 : UART_STOP_BITS_2;
67 | uart_config.flow_ctrl = hw_flowctrl;
68 | uart_config.source_clk = UART_SCLK_APB;
69 | uart_config.rx_flow_ctrl_thresh = 122;
70 |
71 | return uart_config;
72 | }
73 |
74 | void IDFUARTComponent::setup() {
75 | static uint8_t next_uart_num = 0;
76 | #ifdef USE_LOGGER
77 | if (logger::global_logger->get_uart_num() == next_uart_num)
78 | next_uart_num++;
79 | #endif
80 | if (next_uart_num >= UART_NUM_MAX) {
81 | ESP_LOGW(TAG, "Maximum number of UART components created already.");
82 | this->mark_failed();
83 | return;
84 | }
85 | this->uart_num_ = next_uart_num++;
86 | ESP_LOGCONFIG(TAG, "Setting up UART %u...", this->uart_num_);
87 |
88 | this->lock_ = xSemaphoreCreateMutex();
89 |
90 | xSemaphoreTake(this->lock_, portMAX_DELAY);
91 |
92 | uart_config_t uart_config = this->get_config_();
93 | esp_err_t err = uart_param_config(this->uart_num_, &uart_config);
94 | if (err != ESP_OK) {
95 | ESP_LOGW(TAG, "uart_param_config failed: %s", esp_err_to_name(err));
96 | this->mark_failed();
97 | return;
98 | }
99 |
100 | err = uart_driver_install(this->uart_num_, this->rx_buffer_size_, 0, 0, nullptr, 0);
101 | if (err != ESP_OK) {
102 | ESP_LOGW(TAG, "uart_driver_install failed: %s", esp_err_to_name(err));
103 | this->mark_failed();
104 | return;
105 | }
106 |
107 | int8_t tx = this->tx_pin_ != nullptr ? this->tx_pin_->get_pin() : -1;
108 | int8_t rx = this->rx_pin_ != nullptr ? this->rx_pin_->get_pin() : -1;
109 | int8_t cts = this->cts_pin_ != nullptr ? this->cts_pin_->get_pin() : UART_PIN_NO_CHANGE;
110 | int8_t rts = this->rts_pin_ != nullptr ? this->rts_pin_->get_pin() : UART_PIN_NO_CHANGE;
111 |
112 | err = uart_set_pin(this->uart_num_, tx, rx, rts, cts);
113 | if (err != ESP_OK) {
114 | ESP_LOGW(TAG, "uart_set_pin failed: %s", esp_err_to_name(err));
115 | this->mark_failed();
116 | return;
117 | }
118 |
119 | uint32_t invert = 0;
120 | if (this->tx_pin_ != nullptr && this->tx_pin_->is_inverted())
121 | invert |= UART_SIGNAL_TXD_INV;
122 | if (this->rx_pin_ != nullptr && this->rx_pin_->is_inverted())
123 | invert |= UART_SIGNAL_RXD_INV;
124 | if (this->cts_pin_ != nullptr && this->cts_pin_->is_inverted())
125 | invert |= UART_SIGNAL_CTS_INV;
126 | if (this->rts_pin_ != nullptr && this->rts_pin_->is_inverted())
127 | invert |= UART_SIGNAL_RTS_INV;
128 |
129 | err = uart_set_line_inverse(this->uart_num_, invert);
130 | if (err != ESP_OK) {
131 | ESP_LOGW(TAG, "uart_set_line_inverse failed: %s", esp_err_to_name(err));
132 | this->mark_failed();
133 | return;
134 | }
135 |
136 | xSemaphoreGive(this->lock_);
137 | }
138 |
139 | void IDFUARTComponent::dump_config() {
140 | ESP_LOGCONFIG(TAG, "UART Bus:");
141 | ESP_LOGCONFIG(TAG, " Number: %u", this->uart_num_);
142 | LOG_PIN(" TX Pin: ", tx_pin_);
143 | LOG_PIN(" RX Pin: ", rx_pin_);
144 | LOG_PIN(" CTS Pin: ", cts_pin_);
145 | LOG_PIN(" RTS Pin: ", rts_pin_);
146 | if (this->rx_pin_ != nullptr) {
147 | ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_);
148 | }
149 | ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
150 | ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_);
151 | ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_)));
152 | ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
153 | ESP_LOGCONFIG(TAG, " Hardware Flow Control: %s", LOG_STR_ARG(hw_flowctrl_to_str(this->hw_flowctrl_)));
154 | this->check_logger_conflict();
155 | }
156 |
157 | void IDFUARTComponent::write_array(const uint8_t *data, size_t len) {
158 | xSemaphoreTake(this->lock_, portMAX_DELAY);
159 | uart_write_bytes(this->uart_num_, data, len);
160 | xSemaphoreGive(this->lock_);
161 | #ifdef USE_UART_DEBUGGER
162 | for (size_t i = 0; i < len; i++) {
163 | this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
164 | }
165 | #endif
166 | }
167 |
168 | bool IDFUARTComponent::peek_byte(uint8_t *data) {
169 | if (!this->check_read_timeout_())
170 | return false;
171 | xSemaphoreTake(this->lock_, portMAX_DELAY);
172 | if (this->has_peek_)
173 | *data = this->peek_byte_;
174 | else {
175 | int len = uart_read_bytes(this->uart_num_, data, 1, 20 / portTICK_RATE_MS);
176 | if (len == 0) {
177 | *data = 0;
178 | } else {
179 | this->has_peek_ = true;
180 | this->peek_byte_ = *data;
181 | }
182 | }
183 | xSemaphoreGive(this->lock_);
184 | return true;
185 | }
186 |
187 | bool IDFUARTComponent::read_array(uint8_t *data, size_t len) {
188 | size_t length_to_read = len;
189 | if (!this->check_read_timeout_(len))
190 | return false;
191 | xSemaphoreTake(this->lock_, portMAX_DELAY);
192 | if (this->has_peek_) {
193 | length_to_read--;
194 | *data = this->peek_byte_;
195 | data++;
196 | this->has_peek_ = false;
197 | }
198 | if (length_to_read > 0)
199 | uart_read_bytes(this->uart_num_, data, length_to_read, 20 / portTICK_RATE_MS);
200 | xSemaphoreGive(this->lock_);
201 | #ifdef USE_UART_DEBUGGER
202 | for (size_t i = 0; i < len; i++) {
203 | this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
204 | }
205 | #endif
206 | return true;
207 | }
208 |
209 | int IDFUARTComponent::available() {
210 | size_t available;
211 |
212 | xSemaphoreTake(this->lock_, portMAX_DELAY);
213 | uart_get_buffered_data_len(this->uart_num_, &available);
214 | if (this->has_peek_)
215 | available++;
216 | xSemaphoreGive(this->lock_);
217 |
218 | return available;
219 | }
220 |
221 | void IDFUARTComponent::flush() {
222 | ESP_LOGVV(TAG, " Flushing...");
223 | xSemaphoreTake(this->lock_, portMAX_DELAY);
224 | uart_wait_tx_done(this->uart_num_, portMAX_DELAY);
225 | xSemaphoreGive(this->lock_);
226 | }
227 |
228 | void IDFUARTComponent::check_logger_conflict() {}
229 |
230 | } // namespace uart
231 | } // namespace esphome
232 |
233 | #endif // USE_ESP32
234 |
--------------------------------------------------------------------------------
/components/uart/uart_component_esp_idf.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifdef USE_ESP_IDF
4 |
5 | #include
6 | #include "esphome/core/component.h"
7 | #include "uart_component.h"
8 |
9 | namespace esphome {
10 | namespace uart {
11 |
12 | class IDFUARTComponent : public UARTComponent, public Component {
13 | public:
14 | void setup() override;
15 | void dump_config() override;
16 | float get_setup_priority() const override { return setup_priority::BUS; }
17 |
18 | void write_array(const uint8_t *data, size_t len) override;
19 |
20 | bool peek_byte(uint8_t *data) override;
21 | bool read_array(uint8_t *data, size_t len) override;
22 |
23 | int available() override;
24 | void flush() override;
25 |
26 | protected:
27 | void check_logger_conflict() override;
28 | uart_port_t uart_num_;
29 | uart_config_t get_config_();
30 | SemaphoreHandle_t lock_;
31 |
32 | bool has_peek_{false};
33 | uint8_t peek_byte_;
34 | };
35 |
36 | } // namespace uart
37 | } // namespace esphome
38 |
39 | #endif // USE_ESP_IDF
40 |
--------------------------------------------------------------------------------
/components/uart/uart_debugger.cpp:
--------------------------------------------------------------------------------
1 | #include "esphome/core/defines.h"
2 | #ifdef USE_UART_DEBUGGER
3 |
4 | #include
5 | #include "uart_debugger.h"
6 | #include "esphome/core/helpers.h"
7 | #include "esphome/core/log.h"
8 |
9 | namespace esphome {
10 | namespace uart {
11 |
12 | static const char *const TAG = "uart_debug";
13 |
14 | UARTDebugger::UARTDebugger(UARTComponent *parent) {
15 | parent->add_debug_callback([this](UARTDirection direction, uint8_t byte) {
16 | if (!this->is_my_direction_(direction) || this->is_recursive_()) {
17 | return;
18 | }
19 | this->trigger_after_direction_change_(direction);
20 | this->store_byte_(direction, byte);
21 | this->trigger_after_delimiter_(byte);
22 | this->trigger_after_bytes_();
23 | });
24 | }
25 |
26 | void UARTDebugger::loop() { this->trigger_after_timeout_(); }
27 |
28 | bool UARTDebugger::is_my_direction_(UARTDirection direction) {
29 | return this->for_direction_ == UART_DIRECTION_BOTH || this->for_direction_ == direction;
30 | }
31 |
32 | bool UARTDebugger::is_recursive_() { return this->is_triggering_; }
33 |
34 | void UARTDebugger::trigger_after_direction_change_(UARTDirection direction) {
35 | if (this->has_buffered_bytes_() && this->for_direction_ == UART_DIRECTION_BOTH &&
36 | this->last_direction_ != direction) {
37 | this->fire_trigger_();
38 | }
39 | }
40 |
41 | void UARTDebugger::store_byte_(UARTDirection direction, uint8_t byte) {
42 | this->bytes_.push_back(byte);
43 | this->last_direction_ = direction;
44 | this->last_time_ = millis();
45 | }
46 |
47 | void UARTDebugger::trigger_after_delimiter_(uint8_t byte) {
48 | if (this->after_delimiter_.empty() || !this->has_buffered_bytes_()) {
49 | return;
50 | }
51 | if (this->after_delimiter_[this->after_delimiter_pos_] != byte) {
52 | this->after_delimiter_pos_ = 0;
53 | return;
54 | }
55 | this->after_delimiter_pos_++;
56 | if (this->after_delimiter_pos_ == this->after_delimiter_.size()) {
57 | this->fire_trigger_();
58 | this->after_delimiter_pos_ = 0;
59 | }
60 | }
61 |
62 | void UARTDebugger::trigger_after_bytes_() {
63 | if (this->has_buffered_bytes_() && this->after_bytes_ > 0 && this->bytes_.size() >= this->after_bytes_) {
64 | this->fire_trigger_();
65 | }
66 | }
67 |
68 | void UARTDebugger::trigger_after_timeout_() {
69 | if (this->has_buffered_bytes_() && this->after_timeout_ > 0 && millis() - this->last_time_ >= this->after_timeout_) {
70 | this->fire_trigger_();
71 | }
72 | }
73 |
74 | bool UARTDebugger::has_buffered_bytes_() { return !this->bytes_.empty(); }
75 |
76 | void UARTDebugger::fire_trigger_() {
77 | this->is_triggering_ = true;
78 | trigger(this->last_direction_, this->bytes_);
79 | this->bytes_.clear();
80 | this->is_triggering_ = false;
81 | }
82 |
83 | void UARTDummyReceiver::loop() {
84 | // Reading up to a limited number of bytes, to make sure that this loop()
85 | // won't lock up the system on a continuous incoming stream of bytes.
86 | uint8_t data;
87 | int count = 50;
88 | while (this->available() && count--) {
89 | this->read_byte(&data);
90 | }
91 | }
92 |
93 | // In the upcoming log functions, a delay was added after all log calls.
94 | // This is done to allow the system to ship the log lines via the API
95 | // TCP connection(s). Without these delays, debug log lines could go
96 | // missing when UART devices block the main loop for too long.
97 |
98 | void UARTDebug::log_hex(UARTDirection direction, std::vector bytes, uint8_t separator) {
99 | std::string res;
100 | if (direction == UART_DIRECTION_RX) {
101 | res += "<<< ";
102 | } else {
103 | res += ">>> ";
104 | }
105 | size_t len = bytes.size();
106 | char buf[5];
107 | for (size_t i = 0; i < len; i++) {
108 | if (i > 0) {
109 | res += separator;
110 | }
111 | sprintf(buf, "%02X", bytes[i]);
112 | res += buf;
113 | }
114 | ESP_LOGD(TAG, "%s", res.c_str());
115 | delay(10);
116 | }
117 |
118 | void UARTDebug::log_string(UARTDirection direction, std::vector bytes) {
119 | std::string res;
120 | if (direction == UART_DIRECTION_RX) {
121 | res += "<<< \"";
122 | } else {
123 | res += ">>> \"";
124 | }
125 | size_t len = bytes.size();
126 | char buf[5];
127 | for (size_t i = 0; i < len; i++) {
128 | if (bytes[i] == 7) {
129 | res += "\\a";
130 | } else if (bytes[i] == 8) {
131 | res += "\\b";
132 | } else if (bytes[i] == 9) {
133 | res += "\\t";
134 | } else if (bytes[i] == 10) {
135 | res += "\\n";
136 | } else if (bytes[i] == 11) {
137 | res += "\\v";
138 | } else if (bytes[i] == 12) {
139 | res += "\\f";
140 | } else if (bytes[i] == 13) {
141 | res += "\\r";
142 | } else if (bytes[i] == 27) {
143 | res += "\\e";
144 | } else if (bytes[i] == 34) {
145 | res += "\\\"";
146 | } else if (bytes[i] == 39) {
147 | res += "\\'";
148 | } else if (bytes[i] == 92) {
149 | res += "\\\\";
150 | } else if (bytes[i] < 32 || bytes[i] > 127) {
151 | sprintf(buf, "\\x%02X", bytes[i]);
152 | res += buf;
153 | } else {
154 | res += bytes[i];
155 | }
156 | }
157 | res += '"';
158 | ESP_LOGD(TAG, "%s", res.c_str());
159 | delay(10);
160 | }
161 |
162 | void UARTDebug::log_int(UARTDirection direction, std::vector bytes, uint8_t separator) {
163 | std::string res;
164 | size_t len = bytes.size();
165 | if (direction == UART_DIRECTION_RX) {
166 | res += "<<< ";
167 | } else {
168 | res += ">>> ";
169 | }
170 | for (size_t i = 0; i < len; i++) {
171 | if (i > 0) {
172 | res += separator;
173 | }
174 | res += to_string(bytes[i]);
175 | }
176 | ESP_LOGD(TAG, "%s", res.c_str());
177 | delay(10);
178 | }
179 |
180 | void UARTDebug::log_binary(UARTDirection direction, std::vector bytes, uint8_t separator) {
181 | std::string res;
182 | size_t len = bytes.size();
183 | if (direction == UART_DIRECTION_RX) {
184 | res += "<<< ";
185 | } else {
186 | res += ">>> ";
187 | }
188 | char buf[20];
189 | for (size_t i = 0; i < len; i++) {
190 | if (i > 0) {
191 | res += separator;
192 | }
193 | sprintf(buf, "0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(bytes[i]), bytes[i]);
194 | res += buf;
195 | }
196 | ESP_LOGD(TAG, "%s", res.c_str());
197 | delay(10);
198 | }
199 |
200 | } // namespace uart
201 | } // namespace esphome
202 | #endif
203 |
--------------------------------------------------------------------------------
/components/uart/uart_debugger.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "esphome/core/defines.h"
3 | #ifdef USE_UART_DEBUGGER
4 |
5 | #include
6 | #include "esphome/core/component.h"
7 | #include "esphome/core/automation.h"
8 | #include "uart.h"
9 | #include "uart_component.h"
10 |
11 | namespace esphome {
12 | namespace uart {
13 |
14 | /// The UARTDebugger class adds debugging support to a UART bus.
15 | ///
16 | /// It accumulates bytes that travel over the UART bus and triggers one or
17 | /// more actions that can log the data at an appropriate time. What
18 | /// 'appropriate time' means exactly, is determined by a number of
19 | /// configurable constraints. E.g. when a given number of bytes is gathered
20 | /// and/or when no more data has been seen for a given time interval.
21 | class UARTDebugger : public Component, public Trigger> {
22 | public:
23 | explicit UARTDebugger(UARTComponent *parent);
24 | void loop() override;
25 |
26 | /// Set the direction in which to inspect the bytes: incoming, outgoing
27 | /// or both. When debugging in both directions, logging will be triggered
28 | /// when the direction of the data stream changes.
29 | void set_direction(UARTDirection direction) { this->for_direction_ = direction; }
30 |
31 | /// Set the maximum number of bytes to accumulate. When the number of bytes
32 | /// is reached, logging will be triggered.
33 | void set_after_bytes(size_t size) { this->after_bytes_ = size; }
34 |
35 | /// Set a timeout for the data stream. When no new bytes are seen during
36 | /// this timeout, logging will be triggered.
37 | void set_after_timeout(uint32_t timeout) { this->after_timeout_ = timeout; }
38 |
39 | /// Add a delimiter byte. This can be called multiple times to setup a
40 | /// multi-byte delimiter (a typical example would be '\r\n').
41 | /// When the constructued byte sequence is found in the data stream,
42 | /// logging will be triggered.
43 | void add_delimiter_byte(uint8_t byte) { this->after_delimiter_.push_back(byte); }
44 |
45 | protected:
46 | UARTDirection for_direction_;
47 | UARTDirection last_direction_{};
48 | std::vector bytes_{};
49 | size_t after_bytes_;
50 | uint32_t after_timeout_;
51 | uint32_t last_time_{};
52 | std::vector after_delimiter_{};
53 | size_t after_delimiter_pos_{};
54 | bool is_triggering_{false};
55 |
56 | bool is_my_direction_(UARTDirection direction);
57 | bool is_recursive_();
58 | void store_byte_(UARTDirection direction, uint8_t byte);
59 | void trigger_after_direction_change_(UARTDirection direction);
60 | void trigger_after_delimiter_(uint8_t byte);
61 | void trigger_after_bytes_();
62 | void trigger_after_timeout_();
63 | bool has_buffered_bytes_();
64 | void fire_trigger_();
65 | };
66 |
67 | /// This UARTDevice is used by the serial debugger to read data from a
68 | /// serial interface when the 'dummy_receiver' option is enabled.
69 | /// The data are not stored, nor processed. This is most useful when the
70 | /// debugger is used to reverse engineer a serial protocol, for which no
71 | /// specific UARTDevice implementation exists (yet), but for which the
72 | /// incoming bytes must be read to drive the debugger.
73 | class UARTDummyReceiver : public Component, public UARTDevice {
74 | public:
75 | UARTDummyReceiver(UARTComponent *parent) : UARTDevice(parent) {}
76 | void loop() override;
77 | };
78 |
79 | /// This class contains some static methods, that can be used to easily
80 | /// create a logging action for the debugger.
81 | class UARTDebug {
82 | public:
83 | /// Log the bytes as hex values, separated by the provided separator
84 | /// character.
85 | static void log_hex(UARTDirection direction, std::vector bytes, uint8_t separator);
86 |
87 | /// Log the bytes as string values, escaping unprintable characters.
88 | static void log_string(UARTDirection direction, std::vector bytes);
89 |
90 | /// Log the bytes as integer values, separated by the provided separator
91 | /// character.
92 | static void log_int(UARTDirection direction, std::vector bytes, uint8_t separator);
93 |
94 | /// Log the bytes as ' ()' values, separated by the provided
95 | /// separator.
96 | static void log_binary(UARTDirection direction, std::vector bytes, uint8_t separator);
97 | };
98 |
99 | } // namespace uart
100 | } // namespace esphome
101 | #endif
102 |
--------------------------------------------------------------------------------