diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml new file mode 100644 index 0000000..6533d0d --- /dev/null +++ b/.github/workflows/upload_component.yml @@ -0,0 +1,24 @@ +name: Publish to ESP Component Registry + +# Publishes the component to https://components.espressif.com whenever a +# release tag (e.g. v3.1.0) is pushed. Requires an IDF_COMPONENT_API_TOKEN +# secret created from your Espressif registry account. +on: + push: + tags: + - "v*" + workflow_dispatch: + +jobs: + upload: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Upload component + uses: espressif/upload-components-ci-action@v2 + with: + name: "ps2keyboard" + namespace: "lahirunirmalx" + api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9c83f28 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# ESP-IDF / PlatformIO build artifacts +build/ +.pio/ +managed_components/ +dependencies.lock + +# Per-project IDF config (defaults live in sdkconfig.defaults) +sdkconfig +sdkconfig.old + +# Component archive produced by `compote component pack` +dist/ +*.tgz + +# Editor / OS noise +.vscode/ +.idea/ +.DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..28c7f87 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,23 @@ +# Changelog + +All notable changes to this component are documented here. This project +adheres to [Semantic Versioning](https://semver.org/). + +## [3.1.0] - 2026-05 + +### Added +- Native ESP-IDF port: pure `driver/gpio` + `esp_timer`, IRAM ISR, plain C API. +- ESP Component Registry packaging (manifest metadata, examples, LICENSE). + +### Notes +- Supersedes the Arduino-only line for ESP-IDF projects. + +## [3.0.0] - 2026-01 + +### Added +- ESP32 / ESP8266 (Arduino) support. +- Caps / Num / Scroll Lock LED control. + +## [2.x] + +- Multi-platform Arduino library (Uno, Mega, Due, Teensy). diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7287023 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,10 @@ +idf_component_register( + SRCS + "src/ps2keyboard.c" + "src/ps2keymap_us.c" + INCLUDE_DIRS + "include" + REQUIRES + driver + esp_timer +) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f6683e7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,501 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Moe Ghoul, President of Vice + +That's all there is to it! diff --git a/PS2Keyboard.cpp b/PS2Keyboard.cpp deleted file mode 100644 index 53bfa1a..0000000 --- a/PS2Keyboard.cpp +++ /dev/null @@ -1,711 +0,0 @@ -/* - PS2Keyboard.cpp - PS2Keyboard library - Copyright (c) 2007 Free Software Foundation. All right reserved. - Written by Christian Weichel - - ** Mostly rewritten Paul Stoffregen 2010, 2011 - ** Modified for use beginning with Arduino 13 by L. Abraham Smith, * - ** Modified for easy interrup pin assignement on method begin(datapin,irq_pin). Cuningan ** - - for more information you can read the original wiki in arduino.cc - at http://www.arduino.cc/playground/Main/PS2Keyboard - or http://www.pjrc.com/teensy/td_libs_PS2Keyboard.html - - Version 2.4 (March 2013) - - Support Teensy 3.0, Arduino Due, Arduino Leonardo & other boards - - French keyboard layout, David Chochoi, tchoyyfr at yahoo dot fr - - Version 2.3 (October 2011) - - Minor bugs fixed - - Version 2.2 (August 2011) - - Support non-US keyboards - thanks to Rainer Bruch for a German keyboard :) - - Version 2.1 (May 2011) - - timeout to recover from misaligned input - - compatibility with Arduino "new-extension" branch - - TODO: send function, proposed by Scott Penrose, scooterda at me dot com - - Version 2.0 (June 2010) - - Buffering added, many scan codes can be captured without data loss - if your sketch is busy doing other work - - Shift keys supported, completely rewritten scan code to ascii - - Slow linear search replaced with fast indexed table lookups - - Support for Teensy, Arduino Mega, and Sanguino added - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include "PS2Keyboard.h" - -#define BUFFER_SIZE 45 -static volatile uint8_t buffer[BUFFER_SIZE]; -static volatile uint8_t head, tail; -static uint8_t DataPin; -static uint8_t CharBuffer=0; -static uint8_t UTF8next=0; -static const PS2Keymap_t *keymap=NULL; - -// The ISR for the external interrupt -void ps2interrupt(void) -{ - static uint8_t bitcount=0; - static uint8_t incoming=0; - static uint32_t prev_ms=0; - uint32_t now_ms; - uint8_t n, val; - - val = digitalRead(DataPin); - now_ms = millis(); - if (now_ms - prev_ms > 250) { - bitcount = 0; - incoming = 0; - } - prev_ms = now_ms; - n = bitcount - 1; - if (n <= 7) { - incoming |= (val << n); - } - bitcount++; - if (bitcount == 11) { - uint8_t i = head + 1; - if (i >= BUFFER_SIZE) i = 0; - if (i != tail) { - buffer[i] = incoming; - head = i; - } - bitcount = 0; - incoming = 0; - } -} - -static inline uint8_t get_scan_code(void) -{ - uint8_t c, i; - - i = tail; - if (i == head) return 0; - i++; - if (i >= BUFFER_SIZE) i = 0; - c = buffer[i]; - tail = i; - return c; -} - -// http://www.quadibloc.com/comp/scan.htm -// http://www.computer-engineering.org/ps2keyboard/scancodes2.html - -// These arrays provide a simple key map, to turn scan codes into ISO8859 -// output. If a non-US keyboard is used, these may need to be modified -// for the desired output. -// - -const PROGMEM PS2Keymap_t PS2Keymap_US = { - // without shift - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, '`', 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'q', '1', 0, - 0, 0, 'z', 's', 'a', 'w', '2', 0, - 0, 'c', 'x', 'd', 'e', '4', '3', 0, - 0, ' ', 'v', 'f', 't', 'r', '5', 0, - 0, 'n', 'b', 'h', 'g', 'y', '6', 0, - 0, 0, 'm', 'j', 'u', '7', '8', 0, - 0, ',', 'k', 'i', 'o', '0', '9', 0, - 0, '.', '/', 'l', ';', 'p', '-', 0, - 0, 0, '\'', 0, '[', '=', 0, 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, ']', 0, '\\', 0, 0, - 0, 0, 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 }, - // with shift - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, '~', 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'Q', '!', 0, - 0, 0, 'Z', 'S', 'A', 'W', '@', 0, - 0, 'C', 'X', 'D', 'E', '$', '#', 0, - 0, ' ', 'V', 'F', 'T', 'R', '%', 0, - 0, 'N', 'B', 'H', 'G', 'Y', '^', 0, - 0, 0, 'M', 'J', 'U', '&', '*', 0, - 0, '<', 'K', 'I', 'O', ')', '(', 0, - 0, '>', '?', 'L', ':', 'P', '_', 0, - 0, 0, '"', 0, '{', '+', 0, 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '}', 0, '|', 0, 0, - 0, 0, 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 }, - 0 -}; - - -const PROGMEM PS2Keymap_t PS2Keymap_German = { - // without shift - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, '^', 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'q', '1', 0, - 0, 0, 'y', 's', 'a', 'w', '2', 0, - 0, 'c', 'x', 'd', 'e', '4', '3', 0, - 0, ' ', 'v', 'f', 't', 'r', '5', 0, - 0, 'n', 'b', 'h', 'g', 'z', '6', 0, - 0, 0, 'm', 'j', 'u', '7', '8', 0, - 0, ',', 'k', 'i', 'o', '0', '9', 0, - 0, '.', '-', 'l', PS2_o_DIAERESIS, 'p', PS2_SHARP_S, 0, - 0, 0, PS2_a_DIAERESIS, 0, PS2_u_DIAERESIS, '\'', 0, 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '+', 0, '#', 0, 0, - 0, '<', 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 }, - // with shift - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, PS2_DEGREE_SIGN, 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'Q', '!', 0, - 0, 0, 'Y', 'S', 'A', 'W', '"', 0, - 0, 'C', 'X', 'D', 'E', '$', PS2_SECTION_SIGN, 0, - 0, ' ', 'V', 'F', 'T', 'R', '%', 0, - 0, 'N', 'B', 'H', 'G', 'Z', '&', 0, - 0, 0, 'M', 'J', 'U', '/', '(', 0, - 0, ';', 'K', 'I', 'O', '=', ')', 0, - 0, ':', '_', 'L', PS2_O_DIAERESIS, 'P', '?', 0, - 0, 0, PS2_A_DIAERESIS, 0, PS2_U_DIAERESIS, '`', 0, 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '*', 0, '\'', 0, 0, - 0, '>', 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 }, - 1, - // with altgr - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, 0, 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, '@', 0, 0, - 0, 0, 0, 0, 0, 0, PS2_SUPERSCRIPT_TWO, 0, - 0, 0, 0, 0, PS2_CURRENCY_SIGN, 0, PS2_SUPERSCRIPT_THREE, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, PS2_MICRO_SIGN, 0, 0, '{', '[', 0, - 0, 0, 0, 0, 0, '}', ']', 0, - 0, 0, 0, 0, 0, 0, '\\', 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '~', 0, '#', 0, 0, - 0, '|', 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 } -}; - - -const PROGMEM PS2Keymap_t PS2Keymap_French = { - // without shift - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, PS2_SUPERSCRIPT_TWO, 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'a', '&', 0, - 0, 0, 'w', 's', 'q', 'z', PS2_e_ACUTE, 0, - 0, 'c', 'x', 'd', 'e', '\'', '"', 0, - 0, ' ', 'v', 'f', 't', 'r', '(', 0, - 0, 'n', 'b', 'h', 'g', 'y', '-', 0, - 0, 0, ',', 'j', 'u', PS2_e_GRAVE, '_', 0, - 0, ';', 'k', 'i', 'o', PS2_a_GRAVE, PS2_c_CEDILLA, 0, - 0, ':', '!', 'l', 'm', 'p', ')', 0, - 0, 0, PS2_u_GRAVE, 0, '^', '=', 0, 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '$', 0, '*', 0, 0, - 0, '<', 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 }, - // with shift - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, 0, 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'A', '1', 0, - 0, 0, 'W', 'S', 'Q', 'Z', '2', 0, - 0, 'C', 'X', 'D', 'E', '4', '3', 0, - 0, ' ', 'V', 'F', 'T', 'R', '5', 0, - 0, 'N', 'B', 'H', 'G', 'Y', '6', 0, - 0, 0, '?', 'J', 'U', '7', '8', 0, - 0, '.', 'K', 'I', 'O', '0', '9', 0, - 0, '/', PS2_SECTION_SIGN, 'L', 'M', 'P', PS2_DEGREE_SIGN, 0, - 0, 0, '%', 0, PS2_DIAERESIS, '+', 0, 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, PS2_POUND_SIGN, 0, PS2_MICRO_SIGN, 0, 0, - 0, '>', 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 }, - 1, - // with altgr - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, 0, 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, '@', 0, 0, - 0, 0, 0, 0, 0, 0, '~', 0, - 0, 0, 0, 0, 0 /*PS2_EURO_SIGN*/, '{', '#', 0, - 0, 0, 0, 0, 0, 0, '[', 0, - 0, 0, 0, 0, 0, 0, '|', 0, - 0, 0, 0, 0, 0, '`', '\\', 0, - 0, 0, 0, 0, 0, '@', '^', 0, - 0, 0, 0, 0, 0, 0, ']', 0, - 0, 0, 0, 0, 0, 0, '}', 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, PS2_CURRENCY_SIGN, 0, '#', 0, 0, - 0, '|', 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 } -}; - -const PROGMEM PS2Keymap_t PS2Keymap_Spanish = { - // without shift - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, PS2_MASCULINE_ORDINAL, 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'q', '1', 0, - 0, 0, 'z', 's', 'a', 'w', '2', 0, - 0, 'c', 'x', 'd', 'e', '4', '3', 0, - 0, ' ', 'v', 'f', 't', 'r', '5', 0, - 0, 'n', 'b', 'h', 'g', 'y', '6', 0, - 0, 0, 'm', 'j', 'u', '7', '8', 0, - 0, ',', 'k', 'i', 'o', '0', '9', 0, - 0, '.', '-', 'l', 'n', 'p', '\'', 0, - 0, 0, PS2_ACUTE_ACCENT, 0, '`', PS2_INVERTED_EXCLAMATION, 0, 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '+', 0, PS2_c_CEDILLA, 0, 0, - 0, '<', 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 }, - // with shift - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, PS2_FEMININE_ORDINAL, 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'Q', '!', 0, - 0, 0, 'Z', 'S', 'A', 'W', '\"', 0, - 0, 'C', 'X', 'D', 'E', '$', PS2_MIDDLE_DOT, 0, - 0, ' ', 'V', 'F', 'T', 'R', '%', 0, - 0, 'N', 'B', 'H', 'G', 'Y', '&', 0, - 0, 0, 'M', 'J', 'U', '/', '(', 0, - 0, ';', 'K', 'I', 'O', '=', ')', 0, - 0, ':', '_', 'L', 'N', 'P', '?', 0, - 0, 0, PS2_DIAERESIS, 0, '^', PS2_INVERTED_QUESTION_MARK, 0, 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '*', 0, PS2_C_CEDILLA, 0, 0, - 0, '>', 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 }, - 1, - // with altgr - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, '\\', 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'q', '|', 0, - 0, 0, 'z', 's', 'a', 'w', '@', 0, - 0, 'c', 'x', 'd', PS2_EURO_SIGN, '~', '#', 0, - 0, ' ', 'v', 'f', 't', 'r', '5', 0, - 0, 'n', 'b', 'h', 'g', 'y', PS2_NOT_SIGN, 0, - 0, 0, 'm', 'j', 'u', '7', '8', 0, - 0, ',', 'k', 'i', 'o', '0', '9', 0, - 0, '.', '-', 'l', 'n', 'p', '\'', 0, - 0, 0, '{', 0, '[', PS2_INVERTED_EXCLAMATION, 0, 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, ']', 0, '}', 0, 0, - 0, '|', 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 } -}; - -const PROGMEM PS2Keymap_t PS2Keymap_Italian = { - // without shift - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, '\\', 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'q', '1', 0, - 0, 0, 'z', 's', 'a', 'w', '2', 0, - 0, 'c', 'x', 'd', 'e', '4', '3', 0, - 0, ' ', 'v', 'f', 't', 'r', '5', 0, - 0, 'n', 'b', 'h', 'g', 'y', '6', 0, - 0, 0, 'm', 'j', 'u', '7', '8', 0, - 0, ',', 'k', 'i', 'o', '0', '9', 0, - 0, '.', '-', 'l', PS2_o_GRAVE, 'p', '\'', 0, - 0, 0, PS2_a_GRAVE, 0, PS2_e_GRAVE, PS2_i_GRAVE, 0, 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '+', 0, PS2_u_GRAVE, 0, 0, - 0, '<', 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 }, - // with shift - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, '|', 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'Q', '!', 0, - 0, 0, 'Z', 'S', 'A', 'W', '\"', 0, - 0, 'C', 'X', 'D', 'E', '$', PS2_POUND_SIGN, 0, - 0, ' ', 'V', 'F', 'T', 'R', '%', 0, - 0, 'N', 'B', 'H', 'G', 'Y', '&', 0, - 0, 0, 'M', 'J', 'U', '/', '(', 0, - 0, ';', 'K', 'I', 'O', '=', ')', 0, - 0, ':', '_', 'L', PS2_c_CEDILLA, 0, '?', 0, - 0, 0, PS2_DEGREE_SIGN, 0, PS2_e_ACUTE, '^', 0, 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '*', 0, PS2_SECTION_SIGN, 0, 0, - 0, '>', 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 }, - 1, - // with altgr - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, PS2_NOT_SIGN, 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'q', PS2_SUPERSCRIPT_ONE, 0, - 0, 0, 'z', 's', 'a', 'w', PS2_SUPERSCRIPT_TWO, 0, - 0, 'c', 'x', 'd', PS2_EURO_SIGN, PS2_FRACTION_ONE_QUARTER, PS2_SUPERSCRIPT_THREE, 0, - 0, ' ', 'v', 'f', 't', 'r', PS2_FRACTION_ONE_HALF, 0, - 0, 'n', 'b', 'h', 'g', 'y', PS2_NOT_SIGN, 0, - 0, 0, 'm', 'j', 'u', '{', '[', 0, - 0, ',', 'k', 'i', 'o', '}', ']', 0, - 0, '.', '-', 'l', '@', 'p', '\'', 0, - 0, 0, '#', 0, '[', '~', 0, 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, ']', 0, 0, 0, 0, - 0, PS2_LEFT_DOUBLE_ANGLE_QUOTE, 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 } -}; - -const PROGMEM PS2Keymap_t PS2Keymap_UK = { -// https://github.com/PaulStoffregen/PS2Keyboard/issues/15 - // without shift - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, '`', 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'q', '1', 0, - 0, 0, 'z', 's', 'a', 'w', '2', 0, - 0, 'c', 'x', 'd', 'e', '4', '3', 0, - 0, ' ', 'v', 'f', 't', 'r', '5', 0, - 0, 'n', 'b', 'h', 'g', 'y', '6', 0, - 0, 0, 'm', 'j', 'u', '7', '8', 0, - 0, ',', 'k', 'i', 'o', '0', '9', 0, - 0, '.', '/', 'l', ';', 'p', '-', 0, - 0, 0, '\'', 0, '[', '=', 0, 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, ']', 0, '#', 0, 0, - 0, '\\', 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 }, - // with shift - {0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, - 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, 172 /* ¬ */, 0, - 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'Q', '!', 0, - 0, 0, 'Z', 'S', 'A', 'W', '"', 0, - 0, 'C', 'X', 'D', 'E', '$', 163 /* £ */, 0, - 0, ' ', 'V', 'F', 'T', 'R', '%', 0, - 0, 'N', 'B', 'H', 'G', 'Y', '^', 0, - 0, 0, 'M', 'J', 'U', '&', '*', 0, - 0, '<', 'K', 'I', 'O', ')', '(', 0, - 0, '>', '?', 'L', ':', 'P', '_', 0, - 0, 0, '@', 0, '{', '+', 0, 0, - 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER /*Enter*/, '}', 0, '~', 0, 0, - 0, '|', 0, 0, 0, 0, PS2_BACKSPACE, 0, - 0, '1', 0, '4', '7', 0, 0, 0, - '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, - PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, - 0, 0, 0, PS2_F7 }, - 0 -}; - -#define BREAK 0x01 -#define MODIFIER 0x02 -#define SHIFT_L 0x04 -#define SHIFT_R 0x08 -#define ALTGR 0x10 - -static char get_iso8859_code(void) -{ - static uint8_t state=0; - uint8_t s; - char c; - - while (1) { - s = get_scan_code(); - if (!s) return 0; - if (s == 0xF0) { - state |= BREAK; - } else if (s == 0xE0) { - state |= MODIFIER; - } else { - if (state & BREAK) { - if (s == 0x12) { - state &= ~SHIFT_L; - } else if (s == 0x59) { - state &= ~SHIFT_R; - } else if (s == 0x11 && (state & MODIFIER)) { - state &= ~ALTGR; - } - // CTRL, ALT & WIN keys could be added - // but is that really worth the overhead? - state &= ~(BREAK | MODIFIER); - continue; - } - if (s == 0x12) { - state |= SHIFT_L; - continue; - } else if (s == 0x59) { - state |= SHIFT_R; - continue; - } else if (s == 0x11 && (state & MODIFIER)) { - state |= ALTGR; - } - c = 0; - if (state & MODIFIER) { - switch (s) { - case 0x70: c = PS2_INSERT; break; - case 0x6C: c = PS2_HOME; break; - case 0x7D: c = PS2_PAGEUP; break; - case 0x71: c = PS2_DELETE; break; - case 0x69: c = PS2_END; break; - case 0x7A: c = PS2_PAGEDOWN; break; - case 0x75: c = PS2_UPARROW; break; - case 0x6B: c = PS2_LEFTARROW; break; - case 0x72: c = PS2_DOWNARROW; break; - case 0x74: c = PS2_RIGHTARROW; break; - case 0x4A: c = '/'; break; - case 0x5A: c = PS2_ENTER; break; - default: break; - } - } else if ((state & ALTGR) && pgm_read_byte(keymap->uses_altgr)) { - if (s < PS2_KEYMAP_SIZE) - c = pgm_read_byte(keymap->altgr + s); - } else if (state & (SHIFT_L | SHIFT_R)) { - if (s < PS2_KEYMAP_SIZE) - c = pgm_read_byte(keymap->shift + s); - } else { - if (s < PS2_KEYMAP_SIZE) - c = pgm_read_byte(keymap->noshift + s); - } - state &= ~(BREAK | MODIFIER); - if (c) return c; - } - } -} - -bool PS2Keyboard::available() { - if (CharBuffer || UTF8next) return true; - CharBuffer = get_iso8859_code(); - if (CharBuffer) return true; - return false; -} - -void PS2Keyboard::clear() { - CharBuffer = 0; - UTF8next = 0; -} - -uint8_t PS2Keyboard::readScanCode(void) -{ - return get_scan_code(); -} - -int PS2Keyboard::read() { - uint8_t result; - - result = UTF8next; - if (result) { - UTF8next = 0; - } else { - result = CharBuffer; - if (result) { - CharBuffer = 0; - } else { - result = get_iso8859_code(); - } - if (result >= 128) { - UTF8next = (result & 0x3F) | 0x80; - result = ((result >> 6) & 0x1F) | 0xC0; - } - } - if (!result) return -1; - return result; -} - -int PS2Keyboard::readUnicode() { - int result; - - result = CharBuffer; - if (!result) result = get_iso8859_code(); - if (!result) return -1; - UTF8next = 0; - CharBuffer = 0; - return result; -} - -PS2Keyboard::PS2Keyboard() { - // nothing to do here, begin() does it all -} - -void PS2Keyboard::begin(uint8_t data_pin, uint8_t irq_pin, const PS2Keymap_t &map) { - uint8_t irq_num=255; - - DataPin = data_pin; - keymap = ↦ - - // initialize the pins -#ifdef INPUT_PULLUP - pinMode(irq_pin, INPUT_PULLUP); - pinMode(data_pin, INPUT_PULLUP); -#else - pinMode(irq_pin, INPUT); - digitalWrite(irq_pin, HIGH); - pinMode(data_pin, INPUT); - digitalWrite(data_pin, HIGH); -#endif - -#ifdef CORE_INT_EVERY_PIN - irq_num = irq_pin; - -#else - switch(irq_pin) { - #ifdef CORE_INT0_PIN - case CORE_INT0_PIN: - irq_num = 0; - break; - #endif - #ifdef CORE_INT1_PIN - case CORE_INT1_PIN: - irq_num = 1; - break; - #endif - #ifdef CORE_INT2_PIN - case CORE_INT2_PIN: - irq_num = 2; - break; - #endif - #ifdef CORE_INT3_PIN - case CORE_INT3_PIN: - irq_num = 3; - break; - #endif - #ifdef CORE_INT4_PIN - case CORE_INT4_PIN: - irq_num = 4; - break; - #endif - #ifdef CORE_INT5_PIN - case CORE_INT5_PIN: - irq_num = 5; - break; - #endif - #ifdef CORE_INT6_PIN - case CORE_INT6_PIN: - irq_num = 6; - break; - #endif - #ifdef CORE_INT7_PIN - case CORE_INT7_PIN: - irq_num = 7; - break; - #endif - #ifdef CORE_INT8_PIN - case CORE_INT8_PIN: - irq_num = 8; - break; - #endif - #ifdef CORE_INT9_PIN - case CORE_INT9_PIN: - irq_num = 9; - break; - #endif - #ifdef CORE_INT10_PIN - case CORE_INT10_PIN: - irq_num = 10; - break; - #endif - #ifdef CORE_INT11_PIN - case CORE_INT11_PIN: - irq_num = 11; - break; - #endif - #ifdef CORE_INT12_PIN - case CORE_INT12_PIN: - irq_num = 12; - break; - #endif - #ifdef CORE_INT13_PIN - case CORE_INT13_PIN: - irq_num = 13; - break; - #endif - #ifdef CORE_INT14_PIN - case CORE_INT14_PIN: - irq_num = 14; - break; - #endif - #ifdef CORE_INT15_PIN - case CORE_INT15_PIN: - irq_num = 15; - break; - #endif - #ifdef CORE_INT16_PIN - case CORE_INT16_PIN: - irq_num = 16; - break; - #endif - #ifdef CORE_INT17_PIN - case CORE_INT17_PIN: - irq_num = 17; - break; - #endif - #ifdef CORE_INT18_PIN - case CORE_INT18_PIN: - irq_num = 18; - break; - #endif - #ifdef CORE_INT19_PIN - case CORE_INT19_PIN: - irq_num = 19; - break; - #endif - #ifdef CORE_INT20_PIN - case CORE_INT20_PIN: - irq_num = 20; - break; - #endif - #ifdef CORE_INT21_PIN - case CORE_INT21_PIN: - irq_num = 21; - break; - #endif - #ifdef CORE_INT22_PIN - case CORE_INT22_PIN: - irq_num = 22; - break; - #endif - #ifdef CORE_INT23_PIN - case CORE_INT23_PIN: - irq_num = 23; - break; - #endif - } -#endif - - head = 0; - tail = 0; - if (irq_num < 255) { - attachInterrupt(irq_num, ps2interrupt, FALLING); - } -} - - diff --git a/PS2Keyboard.h b/PS2Keyboard.h deleted file mode 100644 index 4931e37..0000000 --- a/PS2Keyboard.h +++ /dev/null @@ -1,232 +0,0 @@ -/* - PS2Keyboard.h - PS2Keyboard library - Copyright (c) 2007 Free Software Foundation. All right reserved. - Written by Christian Weichel - - ** Mostly rewritten Paul Stoffregen , June 2010 - ** Modified for use with Arduino 13 by L. Abraham Smith, * - ** Modified for easy interrup pin assignement on method begin(datapin,irq_pin). Cuningan ** - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - - -#ifndef PS2Keyboard_h -#define PS2Keyboard_h - -#if defined(ARDUINO) && ARDUINO >= 100 -#include "Arduino.h" // for attachInterrupt, FALLING -#else -#include "WProgram.h" -#endif - -#include "utility/int_pins.h" - -// Every call to read() returns a single byte for each -// keystroke. These configure what byte will be returned -// for each "special" key. To ignore a key, use zero. -#define PS2_TAB 9 -#define PS2_ENTER 13 -#define PS2_BACKSPACE 127 -#define PS2_ESC 27 -#define PS2_INSERT 0 -#define PS2_DELETE 127 -#define PS2_HOME 0 -#define PS2_END 0 -#define PS2_PAGEUP 25 -#define PS2_PAGEDOWN 26 -#define PS2_UPARROW 11 -#define PS2_LEFTARROW 8 -#define PS2_DOWNARROW 10 -#define PS2_RIGHTARROW 21 -#define PS2_F1 0 -#define PS2_F2 0 -#define PS2_F3 0 -#define PS2_F4 0 -#define PS2_F5 0 -#define PS2_F6 0 -#define PS2_F7 0 -#define PS2_F8 0 -#define PS2_F9 0 -#define PS2_F10 0 -#define PS2_F11 0 -#define PS2_F12 0 -#define PS2_SCROLL 0 -#define PS2_EURO_SIGN 0 - -#define PS2_INVERTED_EXCLAMATION 161 // ¡ -#define PS2_CENT_SIGN 162 // ¢ -#define PS2_POUND_SIGN 163 // £ -#define PS2_CURRENCY_SIGN 164 // ¤ -#define PS2_YEN_SIGN 165 // ¥ -#define PS2_BROKEN_BAR 166 // ¦ -#define PS2_SECTION_SIGN 167 // § -#define PS2_DIAERESIS 168 // ¨ -#define PS2_COPYRIGHT_SIGN 169 // © -#define PS2_FEMININE_ORDINAL 170 // ª -#define PS2_LEFT_DOUBLE_ANGLE_QUOTE 171 // « -#define PS2_NOT_SIGN 172 // ¬ -#define PS2_HYPHEN 173 -#define PS2_REGISTERED_SIGN 174 // ® -#define PS2_MACRON 175 // ¯ -#define PS2_DEGREE_SIGN 176 // ° -#define PS2_PLUS_MINUS_SIGN 177 // ± -#define PS2_SUPERSCRIPT_TWO 178 // ² -#define PS2_SUPERSCRIPT_THREE 179 // ³ -#define PS2_ACUTE_ACCENT 180 // ´ -#define PS2_MICRO_SIGN 181 // µ -#define PS2_PILCROW_SIGN 182 // ¶ -#define PS2_MIDDLE_DOT 183 // · -#define PS2_CEDILLA 184 // ¸ -#define PS2_SUPERSCRIPT_ONE 185 // ¹ -#define PS2_MASCULINE_ORDINAL 186 // º -#define PS2_RIGHT_DOUBLE_ANGLE_QUOTE 187 // » -#define PS2_FRACTION_ONE_QUARTER 188 // ¼ -#define PS2_FRACTION_ONE_HALF 189 // ½ -#define PS2_FRACTION_THREE_QUARTERS 190 // ¾ -#define PS2_INVERTED_QUESTION_MARK 191 // ¿ -#define PS2_A_GRAVE 192 // À -#define PS2_A_ACUTE 193 // Á -#define PS2_A_CIRCUMFLEX 194 // Â -#define PS2_A_TILDE 195 // Ã -#define PS2_A_DIAERESIS 196 // Ä -#define PS2_A_RING_ABOVE 197 // Å -#define PS2_AE 198 // Æ -#define PS2_C_CEDILLA 199 // Ç -#define PS2_E_GRAVE 200 // È -#define PS2_E_ACUTE 201 // É -#define PS2_E_CIRCUMFLEX 202 // Ê -#define PS2_E_DIAERESIS 203 // Ë -#define PS2_I_GRAVE 204 // Ì -#define PS2_I_ACUTE 205 // Í -#define PS2_I_CIRCUMFLEX 206 // Î -#define PS2_I_DIAERESIS 207 // Ï -#define PS2_ETH 208 // Ð -#define PS2_N_TILDE 209 // Ñ -#define PS2_O_GRAVE 210 // Ò -#define PS2_O_ACUTE 211 // Ó -#define PS2_O_CIRCUMFLEX 212 // Ô -#define PS2_O_TILDE 213 // Õ -#define PS2_O_DIAERESIS 214 // Ö -#define PS2_MULTIPLICATION 215 // × -#define PS2_O_STROKE 216 // Ø -#define PS2_U_GRAVE 217 // Ù -#define PS2_U_ACUTE 218 // Ú -#define PS2_U_CIRCUMFLEX 219 // Û -#define PS2_U_DIAERESIS 220 // Ü -#define PS2_Y_ACUTE 221 // Ý -#define PS2_THORN 222 // Þ -#define PS2_SHARP_S 223 // ß -#define PS2_a_GRAVE 224 // à -#define PS2_a_ACUTE 225 // á -#define PS2_a_CIRCUMFLEX 226 // â -#define PS2_a_TILDE 227 // ã -#define PS2_a_DIAERESIS 228 // ä -#define PS2_a_RING_ABOVE 229 // å -#define PS2_ae 230 // æ -#define PS2_c_CEDILLA 231 // ç -#define PS2_e_GRAVE 232 // è -#define PS2_e_ACUTE 233 // é -#define PS2_e_CIRCUMFLEX 234 // ê -#define PS2_e_DIAERESIS 235 // ë -#define PS2_i_GRAVE 236 // ì -#define PS2_i_ACUTE 237 // í -#define PS2_i_CIRCUMFLEX 238 // î -#define PS2_i_DIAERESIS 239 // ï -#define PS2_eth 240 // ð -#define PS2_n_TILDE 241 // ñ -#define PS2_o_GRAVE 242 // ò -#define PS2_o_ACUTE 243 // ó -#define PS2_o_CIRCUMFLEX 244 // ô -#define PS2_o_TILDE 245 // õ -#define PS2_o_DIAERESIS 246 // ö -#define PS2_DIVISION 247 // ÷ -#define PS2_o_STROKE 248 // ø -#define PS2_u_GRAVE 249 // ù -#define PS2_u_ACUTE 250 // ú -#define PS2_u_CIRCUMFLEX 251 // û -#define PS2_u_DIAERESIS 252 // ü -#define PS2_y_ACUTE 253 // ý -#define PS2_thorn 254 // þ -#define PS2_y_DIAERESIS 255 // ÿ - - -#define PS2_KEYMAP_SIZE 136 - -typedef struct { - uint8_t noshift[PS2_KEYMAP_SIZE]; - uint8_t shift[PS2_KEYMAP_SIZE]; - unsigned int uses_altgr; - /* - * "uint8_t uses_altgr;" makes the ESP8266 - NodeMCU modules crash. - * So, I replaced it with an int and... It works! - * I think it's because of the 32-bit architecture of the ESP8266 - * and the use of the flash memory to store the keymaps. - * Maybe I'm wrong, it remains a hypothesis. - */ - uint8_t altgr[PS2_KEYMAP_SIZE]; -} PS2Keymap_t; - - -extern const PROGMEM PS2Keymap_t PS2Keymap_US; -extern const PROGMEM PS2Keymap_t PS2Keymap_German; -extern const PROGMEM PS2Keymap_t PS2Keymap_French; -extern const PROGMEM PS2Keymap_t PS2Keymap_Spanish; -extern const PROGMEM PS2Keymap_t PS2Keymap_Italian; -extern const PROGMEM PS2Keymap_t PS2Keymap_UK; - - -/** - * Purpose: Provides an easy access to PS2 keyboards - * Author: Christian Weichel - */ -class PS2Keyboard { - public: - /** - * This constructor does basically nothing. Please call the begin(int,int) - * method before using any other method of this class. - */ - PS2Keyboard(); - - /** - * Starts the keyboard "service" by registering the external interrupt. - * setting the pin modes correctly and driving those needed to high. - * The propably best place to call this method is in the setup routine. - */ - static void begin(uint8_t dataPin, uint8_t irq_pin, const PS2Keymap_t &map = PS2Keymap_US); - - /** - * Returns true if there is a char to be read, false if not. - */ - static bool available(); - - /* Discards any received data, sets available() to false without a call to read() - */ - static void clear(); - - /** - * Retutns ps2 scan code. - */ - static uint8_t readScanCode(void); - - /** - * Returns the char last read from the keyboard. - * If there is no char available, -1 is returned. - */ - static int read(); - static int readUnicode(); -}; - -#endif diff --git a/README.md b/README.md index 931f685..3928ad1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,149 @@ -#PS/2 Keyboard Library# +# ps2keyboard - ESP-IDF Component -PS2Keyboard allows you to use a keyboard for user input. +[![License: LGPL v2.1](https://img.shields.io/badge/License-LGPL%20v2.1-blue.svg)](https://www.gnu.org/licenses/lgpl-2.1) +[![Version](https://img.shields.io/badge/version-3.1.0-green.svg)](https://github.com/lahirunirmalx/PS2Keyboard) -http://www.pjrc.com/teensy/td_libs_PS2Keyboard.html +A native ESP-IDF component for reading from a PS/2 keyboard on the ESP32 +family. Ported from the long-standing Arduino `PS2Keyboard` library. -![](http://www.pjrc.com/teensy/td_libs_PS2Keyboard.jpg) +## Features + +- Pure ESP-IDF: uses `driver/gpio`, `esp_timer` and the IDF GPIO ISR service +- Interrupt-driven scan-code capture (`IRAM_ATTR` ISR, runs from IRAM) +- ISO-8859-1 character decoding with Shift / AltGr / Caps / Num / Scroll Lock +- LED control (Caps / Num / Scroll Lock) by bit-banging the host-to-device line +- Plain C API, no C++ runtime required +- Supports any ESP32 variant (S2 / S3 / C3 / C6 / H2 / classic) + +## Installation + +### ESP-IDF Component Manager (recommended) + +Install from the [ESP Component Registry](https://components.espressif.com/components/lahirunirmalx/ps2keyboard): + +```bash +idf.py add-dependency "lahirunirmalx/ps2keyboard^3.1.0" +``` + +Or declare it in your project's `idf_component.yml`: + +```yaml +dependencies: + lahirunirmalx/ps2keyboard: "^3.1.0" +``` + +### Drop-in component + +Alternatively, clone (or submodule) this repository under your project's +`components/` directory: + +```bash +mkdir -p components +git clone https://github.com/lahirunirmalx/PS2Keyboard.git components/ps2keyboard +``` + +## Wiring + +| PS/2 Pin | Signal | Connect To | +|----------|--------|-------------------------------------| +| 1 | DATA | `data_pin` GPIO | +| 3 | GND | GND | +| 4 | VCC | 5 V (PS/2 keyboards expect 5 V) | +| 5 | CLOCK | `clock_pin` GPIO (interrupt source) | + +> **Note:** ESP32 GPIOs are 3.3 V. PS/2 keyboards are 5 V open-collector with +> internal pull-ups; in practice the lines idle high and the keyboard sinks +> them low, so most modern ESP32 boards work directly. For long-term +> reliability use a level shifter or a series resistor + clamping diode. + +## Quick Start + +```c +#include "ps2keyboard.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#define PS2_DATA_PIN GPIO_NUM_18 +#define PS2_CLOCK_PIN GPIO_NUM_19 + +void app_main(void) +{ + ESP_ERROR_CHECK(ps2keyboard_begin(PS2_DATA_PIN, PS2_CLOCK_PIN, NULL)); + + while (1) { + if (ps2keyboard_available()) { + int c = ps2keyboard_read(); + if (c == PS2_ENTER) printf("\n"); + else if (c == PS2_TAB) printf("[Tab]"); + else if (c == PS2_ESC) printf("[ESC]"); + else if (c > 0) putchar(c); + fflush(stdout); + } else { + vTaskDelay(pdMS_TO_TICKS(5)); + } + } +} +``` + +A complete buildable example lives in [examples/simple_test](examples/simple_test/). +Build it with: + +```bash +cd examples/simple_test +idf.py set-target esp32 +idf.py build flash monitor +``` + +## API Reference + +```c +esp_err_t ps2keyboard_begin(gpio_num_t data_pin, + gpio_num_t clock_pin, + const ps2_keymap_t *keymap); +void ps2keyboard_end(void); + +bool ps2keyboard_available(void); +void ps2keyboard_clear(void); + +uint8_t ps2keyboard_read_scancode(void); /* raw scan code */ +int ps2keyboard_read(void); /* UTF-8 byte */ +int ps2keyboard_read_unicode(void); /* code point */ +``` + +Pass `NULL` as the keymap to use the bundled `ps2_keymap_us`. Custom layouts +can be supplied as `const ps2_keymap_t` instances. + +### Special key constants + +`PS2_ENTER`, `PS2_TAB`, `PS2_ESC`, `PS2_BACKSPACE`, `PS2_DELETE`, `PS2_INSERT`, +`PS2_HOME`, `PS2_END`, `PS2_PAGEUP`, `PS2_PAGEDOWN`, `PS2_UPARROW`, +`PS2_DOWNARROW`, `PS2_LEFTARROW`, `PS2_RIGHTARROW`, `PS2_F1`..`PS2_F12`, +`PS2_SCROLL`. + +## Notes on the GPIO ISR service + +`ps2keyboard_begin()` calls `gpio_install_isr_service(ESP_INTR_FLAG_IRAM)`. If +your application has already installed the service with different flags this +call returns `ESP_ERR_INVALID_STATE`, which is treated as success - the +existing service is reused. + +## Version History + +- **v3.1.0** (May 2026) — Native ESP-IDF port (this branch) +- **v3.0.0** (January 2026) — ESP32 / ESP8266 (Arduino) support, Lock-key LEDs +- **v2.x** — Multi-platform Arduino library (Uno, Mega, Due, Teensy) + +## Credits + +Original library by [PJRC](http://www.pjrc.com/teensy/td_libs_PS2Keyboard.html). + +- Christian Weichel — original author +- Paul Stoffregen — major rewrite +- L. Abraham Smith — Arduino 13 modifications +- Cuningan — flexible pin assignment +- Lahiru Nirmal — ESP32 / ESP8266 (Arduino) port and ESP-IDF port + +## License + +GNU Lesser General Public License v2.1 +([LGPL-2.1](https://www.gnu.org/licenses/lgpl-2.1.html)). diff --git a/examples/International/International.ino b/examples/International/International.ino deleted file mode 100644 index 48fba5f..0000000 --- a/examples/International/International.ino +++ /dev/null @@ -1,34 +0,0 @@ -/* PS2Keyboard library, International Keyboard Layout Example - http://www.pjrc.com/teensy/td_libs_PS2Keyboard.html - - keyboard.begin() accepts an optional 3rd parameter to - configure the PS2 keyboard layout. Uncomment the line for - your keyboard. If it doesn't exist, you can create it in - PS2Keyboard.cpp and email paul@pjrc.com to have it included - in future versions of this library. -*/ - -#include - -const int DataPin = 8; -const int IRQpin = 5; - -PS2Keyboard keyboard; - -void setup() { - //keyboard.begin(DataPin, IRQpin, PS2Keymap_US); - keyboard.begin(DataPin, IRQpin, PS2Keymap_German); - //keyboard.begin(DataPin, IRQpin, PS2Keymap_French); - //keyboard.begin(DataPin, IRQpin, PS2Keymap_Spanish); - //keyboard.begin(DataPin, IRQpin, PS2Keymap_Italian); - //keyboard.begin(DataPin, IRQpin, PS2Keymap_UK); - Serial.begin(9600); - Serial.println("International Keyboard Test:"); -} - -void loop() { - if (keyboard.available()) { - char c = keyboard.read(); - Serial.print(c); - } -} diff --git a/examples/Simple_Test/Simple_Test.ino b/examples/Simple_Test/Simple_Test.ino deleted file mode 100644 index bd28b4b..0000000 --- a/examples/Simple_Test/Simple_Test.ino +++ /dev/null @@ -1,75 +0,0 @@ -/* PS2Keyboard library example - - PS2Keyboard now requries both pins specified for begin() - - keyboard.begin(data_pin, irq_pin); - - Valid irq pins: - Arduino Uno: 2, 3 - Arduino Due: All pins, except 13 (LED) - Arduino Mega: 2, 3, 18, 19, 20, 21 - Teensy 3.0: All pins, except 13 (LED) - Teensy 2.0: 5, 6, 7, 8 - Teensy 1.0: 0, 1, 2, 3, 4, 6, 7, 16 - Teensy++ 2.0: 0, 1, 2, 3, 18, 19, 36, 37 - Teensy++ 1.0: 0, 1, 2, 3, 18, 19, 36, 37 - Sanguino: 2, 10, 11 - - for more information you can read the original wiki in arduino.cc - at http://www.arduino.cc/playground/Main/PS2Keyboard - or http://www.pjrc.com/teensy/td_libs_PS2Keyboard.html - - Like the Original library and example this is under LGPL license. - - Modified by Cuninganreset@gmail.com on 2010-03-22 - Modified by Paul Stoffregen June 2010 -*/ - -#include - -const int DataPin = 8; -const int IRQpin = 5; - -PS2Keyboard keyboard; - -void setup() { - delay(1000); - keyboard.begin(DataPin, IRQpin); - Serial.begin(9600); - Serial.println("Keyboard Test:"); -} - -void loop() { - if (keyboard.available()) { - - // read the next key - char c = keyboard.read(); - - // check for some of the special keys - if (c == PS2_ENTER) { - Serial.println(); - } else if (c == PS2_TAB) { - Serial.print("[Tab]"); - } else if (c == PS2_ESC) { - Serial.print("[ESC]"); - } else if (c == PS2_PAGEDOWN) { - Serial.print("[PgDn]"); - } else if (c == PS2_PAGEUP) { - Serial.print("[PgUp]"); - } else if (c == PS2_LEFTARROW) { - Serial.print("[Left]"); - } else if (c == PS2_RIGHTARROW) { - Serial.print("[Right]"); - } else if (c == PS2_UPARROW) { - Serial.print("[Up]"); - } else if (c == PS2_DOWNARROW) { - Serial.print("[Down]"); - } else if (c == PS2_DELETE) { - Serial.print("[Del]"); - } else { - - // otherwise, just print all normal characters - Serial.print(c); - } - } -} diff --git a/examples/TypeToDisplay/TypeToDisplay.ino b/examples/TypeToDisplay/TypeToDisplay.ino deleted file mode 100644 index 2b28f15..0000000 --- a/examples/TypeToDisplay/TypeToDisplay.ino +++ /dev/null @@ -1,218 +0,0 @@ -/* TypeToDisplay - PS2Keyboard keystrokes show on a standard LCD Display - - Lcd support added 18/2/2018 D.R.Patterson - lcd character set: - http://forum.arduino.cc/index.php?topic=19002.0 - - Tested on a mega with a Keypad Shield for Arduino - - PS2Keyboard now requries both pins specified for begin() - keyboard.begin(data_pin, irq_pin); - - Valid irq pins: - Arduino Uno: 2, 3 - Arduino Due: All pins, except 13 (LED) - Arduino Mega: 2, 3, 18, 19, 20, 21 - Teensy 2.0: All pins, except 13 (LED) - Teensy 2.0: 5, 6, 7, 8 - Teensy 1.0: 0, 1, 2, 3, 4, 6, 7, 16 - Teensy++ 2.0: 0, 1, 2, 3, 18, 19, 36, 37 - Teensy++ 1.0: 0, 1, 2, 3, 18, 19, 36, 37 - Sanguino: 2, 10, 11 - - for more information you can read the original wiki in arduino.cc - at http://www.arduino.cc/playground/Main/PS2Keyboard - or http://www.pjrc.com/teensy/td_libs_PS2Keyboard.html - - Like the Original library and example this is under LGPL license. - - Modified by Cuninganreset@gmail.com on 2010-03-22 - Modified by Paul Stoffregen June 2010 -*/ - -#include -const int DataPin = 19; -const int IRQpin = 18; -PS2Keyboard keyboard; - -#include -// LCD Keypad Shield for Arduino -// http://www.hobbytronics.co.uk/lcd/lcd-displays-5v/arduino-lcd-keypad-shield - -//set constants for number of rows and columns to match your LCD -const int numRows = 2; -const int numCols = 16; -// initialize the library with the numbers of the interface pins -LiquidCrystal lcd(8, 9, 4, 5, 6, 7); - -byte x, y; // track lcd position - -// custom pound using https://omerk.github.io/lcdchargen/ -byte customPound[8] = { - 0b01100, - 0b10010, - 0b01000, - 0b11100, - 0b01000, - 0b01000, - 0b11110, - 0b00000 -}; -const char pound = 1; - -byte customkey[8] = { // ¬ - 0b00000, - 0b00000, - 0b00000, - 0b00000, - 0b11111, - 0b00001, - 0b00001, - 0b00000 -}; -const char custom = 2; - -byte customslash[8] = { // backslash - 0b00000, - 0b00000, - 0b10000, - 0b01000, - 0b00100, - 0b00010, - 0b00001, - 0b00000 -}; -const char backslash = 3; - -char c; - -// currently un-used: -#define is_printable(c) (!(c&0x80)) // don't print if top bit is set - -void setup() { - delay(1000); - keyboard.begin(DataPin, IRQpin); - Serial.begin(115200); - while (!Serial) yield(); - lcd.begin(numRows, numCols); - delay(250); - lcd.clear(); - // create a new custom character - lcd.createChar(pound, customPound); - lcd.createChar(custom, customkey); - lcd.createChar(backslash, customslash); - lcd.cursor(); // Enable Cursor - //lcd.blink(); // Blinking cursor - lcd.clear(); - - lcd.print(F("Keyboard Test:")); - lcd.setCursor(0, 1); - lcd.print(F("Esc to clear LCD")); - Serial.println(F("Keyboard Test:")); - unsigned long t = millis(); - do { - if (keyboard.available()) { - c = keyboard.read(); - if (c == PS2_ESC) break; - } - } while ((millis() - t) < 8000); - lcd.clear(); -} - -void loop() { - - if (keyboard.available()) { - // read the next key - c = keyboard.read(); - - // check for some of the special keys - if (c == PS2_ENTER) { - Serial.println(); - x = 0; - y += 1; - if (y == numRows) { - y = 0; - lcd.clear(); - } - lcd.setCursor(x, y); - - } else if (c == PS2_TAB) { - lcdprint(F("[Tab]")); - - } else if (c == PS2_ESC) { - Serial.println(F("\n[ESC]\n")); - lcd.clear(); - x = 0; y = 0; - lcd.setCursor(x, y); - - } else if (c == PS2_PAGEDOWN) { - lcdprint(F("[PgDn]")); - - } else if (c == PS2_PAGEUP) { - lcdprint(F("[PgUp]")); - - } else if (c == PS2_LEFTARROW) { - lcdprint(F("[Left]")); - - } else if (c == PS2_RIGHTARROW) { - lcdprint(F("[Right]")); - - } else if (c == PS2_UPARROW) { - lcdprint(F("[Up]")); - - } else if (c == PS2_DOWNARROW) { - lcdprint(F("[Down]")); - - } else if (c == PS2_DELETE) { - lcdprint(F("[Del]")); - - } else if (c == PS2_BACKSPACE) { - lcdprint(F("[Back]")); - - } else { - // otherwise, just print all normal characters - lcdprintChar(c); - } - } -} - -void lcdprintChar(char t) { // display char on lcd and Serial - byte test = byte(t); - if (test == 194) return; // this char appears as an indicator of special keys - - if (x > (numCols - 1) ) { - x = 0; - if (y == (numRows - 1)) { - lcd.clear(); - Serial.println(); - y = 0; - } else y += 1; - Serial.println(); - lcd.setCursor(x, y); - } - - if (test == 126) lcd.write(243); // 126 ~ - else if (test == 163) lcd.write(pound); // 163 £ - else if (test == 172) lcd.write(custom); // 194 172 ¬ - else if (test == 92) lcd.write(backslash); // 92 backslash - else lcd.print(t); - Serial.print(t); - x += 1; -} - -void lcdprint(String t) { // display string on lcd and Serial - byte L = t.length(); - if (x > (numCols - L) ) { - x = 0; - if (y == (numRows - 1)) { - lcd.clear(); - Serial.println(); - y = 0; - } else y += 1; - Serial.println(); - lcd.setCursor(x, y); - } - lcd.print(t); - Serial.print(t); - x += L; -} diff --git a/examples/simple_test/CMakeLists.txt b/examples/simple_test/CMakeLists.txt new file mode 100644 index 0000000..cb7e69d --- /dev/null +++ b/examples/simple_test/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.16) + +# Pull the parent directory in as an extra component so the example can +# reference `ps2keyboard` without having to be installed via the Component +# Manager. +set(EXTRA_COMPONENT_DIRS "${CMAKE_CURRENT_LIST_DIR}/../..") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ps2keyboard_simple_test) diff --git a/examples/simple_test/main/CMakeLists.txt b/examples/simple_test/main/CMakeLists.txt new file mode 100644 index 0000000..b4c0552 --- /dev/null +++ b/examples/simple_test/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "main.c" + INCLUDE_DIRS "." + REQUIRES ps2keyboard +) diff --git a/examples/simple_test/main/main.c b/examples/simple_test/main/main.c new file mode 100644 index 0000000..c6c7c88 --- /dev/null +++ b/examples/simple_test/main/main.c @@ -0,0 +1,60 @@ +/* + PS/2 Keyboard ESP-IDF Example - simple_test + + Wiring: + PS/2 DATA -> CONFIG_PS2_DATA_PIN (default GPIO 18) + PS/2 CLOCK -> CONFIG_PS2_CLOCK_PIN (default GPIO 19) + PS/2 VCC -> 5V (level-shift to 3.3V if your board cannot tolerate 5V on GPIOs) + PS/2 GND -> GND + + License: LGPL v2.1 +*/ + +#include + +#include "esp_err.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "ps2keyboard.h" + +#define PS2_DATA_PIN GPIO_NUM_18 +#define PS2_CLOCK_PIN GPIO_NUM_19 + +static const char *TAG = "ps2_example"; + +void app_main(void) +{ + vTaskDelay(pdMS_TO_TICKS(1000)); + + ESP_ERROR_CHECK(ps2keyboard_begin(PS2_DATA_PIN, PS2_CLOCK_PIN, NULL)); + ESP_LOGI(TAG, "PS/2 keyboard ready - type to begin:"); + + while (1) { + if (ps2keyboard_available()) { + int c = ps2keyboard_read(); + + switch (c) { + case PS2_ENTER: printf("\n"); break; + case PS2_TAB: printf("[Tab]"); break; + case PS2_ESC: printf("[ESC]"); break; + case PS2_PAGEDOWN: printf("[PgDn]"); break; + case PS2_PAGEUP: printf("[PgUp]"); break; + case PS2_LEFTARROW: printf("[Left]"); break; + case PS2_RIGHTARROW: printf("[Right]"); break; + case PS2_UPARROW: printf("[Up]"); break; + case PS2_DOWNARROW: printf("[Down]"); break; + case PS2_DELETE: printf("[Del]"); break; + default: + if (c > 0) { + putchar(c); + } + break; + } + fflush(stdout); + } else { + vTaskDelay(pdMS_TO_TICKS(5)); + } + } +} diff --git a/examples/simple_test/sdkconfig.defaults b/examples/simple_test/sdkconfig.defaults new file mode 100644 index 0000000..8e326e3 --- /dev/null +++ b/examples/simple_test/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT_INIT=n diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 0000000..1dc4f3f --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,19 @@ +version: "3.1.0" +description: "PS/2 keyboard driver for ESP-IDF (ESP32 family). Provides scan code reading, ISO-8859 character translation, modifier handling and Caps/Num/Scroll Lock LED control." +url: "https://github.com/lahirunirmalx/PS2Keyboard" +repository: "https://github.com/lahirunirmalx/PS2Keyboard.git" +documentation: "https://github.com/lahirunirmalx/PS2Keyboard#readme" +issues: "https://github.com/lahirunirmalx/PS2Keyboard/issues" +license: "LGPL-2.1-or-later" +maintainers: + - "Lahiru " +tags: + - ps2 + - keyboard + - input + - hid +dependencies: + idf: + version: ">=4.4" +examples: + - path: ./examples/simple_test diff --git a/include/ps2keyboard.h b/include/ps2keyboard.h new file mode 100644 index 0000000..f6bd979 --- /dev/null +++ b/include/ps2keyboard.h @@ -0,0 +1,227 @@ +/* + ps2keyboard.h - PS/2 keyboard driver for ESP-IDF + Copyright (c) 2007 Free Software Foundation. All rights reserved. + + Originally written by Christian Weichel . + Mostly rewritten by Paul Stoffregen , June 2010. + Modified for Arduino 13 by L. Abraham Smith . + Modified for easy interrupt pin assignment by Cuningan . + Modified for ESP32/ESP8266 (Arduino) by Lahiru . + Ported to native ESP-IDF by Lahiru , 2026. + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. +*/ + +#ifndef PS2KEYBOARD_H +#define PS2KEYBOARD_H + +#include +#include + +#include "driver/gpio.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Special key codes returned by ps2keyboard_read(). Set to 0 to ignore. */ +#define PS2_TAB 9 +#define PS2_ENTER 13 +#define PS2_BACKSPACE 127 +#define PS2_ESC 27 +#define PS2_INSERT 0 +#define PS2_DELETE 127 +#define PS2_HOME 0 +#define PS2_END 0 +#define PS2_PAGEUP 25 +#define PS2_PAGEDOWN 26 +#define PS2_UPARROW 11 +#define PS2_LEFTARROW 8 +#define PS2_DOWNARROW 10 +#define PS2_RIGHTARROW 21 +#define PS2_F1 0 +#define PS2_F2 0 +#define PS2_F3 0 +#define PS2_F4 0 +#define PS2_F5 0 +#define PS2_F6 0 +#define PS2_F7 0 +#define PS2_F8 0 +#define PS2_F9 0 +#define PS2_F10 0 +#define PS2_F11 0 +#define PS2_F12 0 +#define PS2_SCROLL 0 +#define PS2_EURO_SIGN 0 + +/* ISO-8859-1 (Latin-1) symbols, available via shift / AltGr in some keymaps. */ +#define PS2_INVERTED_EXCLAMATION 161 +#define PS2_CENT_SIGN 162 +#define PS2_POUND_SIGN 163 +#define PS2_CURRENCY_SIGN 164 +#define PS2_YEN_SIGN 165 +#define PS2_BROKEN_BAR 166 +#define PS2_SECTION_SIGN 167 +#define PS2_DIAERESIS 168 +#define PS2_COPYRIGHT_SIGN 169 +#define PS2_FEMININE_ORDINAL 170 +#define PS2_LEFT_DOUBLE_ANGLE_QUOTE 171 +#define PS2_NOT_SIGN 172 +#define PS2_HYPHEN 173 +#define PS2_REGISTERED_SIGN 174 +#define PS2_MACRON 175 +#define PS2_DEGREE_SIGN 176 +#define PS2_PLUS_MINUS_SIGN 177 +#define PS2_SUPERSCRIPT_TWO 178 +#define PS2_SUPERSCRIPT_THREE 179 +#define PS2_ACUTE_ACCENT 180 +#define PS2_MICRO_SIGN 181 +#define PS2_PILCROW_SIGN 182 +#define PS2_MIDDLE_DOT 183 +#define PS2_CEDILLA 184 +#define PS2_SUPERSCRIPT_ONE 185 +#define PS2_MASCULINE_ORDINAL 186 +#define PS2_RIGHT_DOUBLE_ANGLE_QUOTE 187 +#define PS2_FRACTION_ONE_QUARTER 188 +#define PS2_FRACTION_ONE_HALF 189 +#define PS2_FRACTION_THREE_QUARTERS 190 +#define PS2_INVERTED_QUESTION_MARK 191 +#define PS2_A_GRAVE 192 +#define PS2_A_ACUTE 193 +#define PS2_A_CIRCUMFLEX 194 +#define PS2_A_TILDE 195 +#define PS2_A_DIAERESIS 196 +#define PS2_A_RING_ABOVE 197 +#define PS2_AE 198 +#define PS2_C_CEDILLA 199 +#define PS2_E_GRAVE 200 +#define PS2_E_ACUTE 201 +#define PS2_E_CIRCUMFLEX 202 +#define PS2_E_DIAERESIS 203 +#define PS2_I_GRAVE 204 +#define PS2_I_ACUTE 205 +#define PS2_I_CIRCUMFLEX 206 +#define PS2_I_DIAERESIS 207 +#define PS2_ETH 208 +#define PS2_N_TILDE 209 +#define PS2_O_GRAVE 210 +#define PS2_O_ACUTE 211 +#define PS2_O_CIRCUMFLEX 212 +#define PS2_O_TILDE 213 +#define PS2_O_DIAERESIS 214 +#define PS2_MULTIPLICATION 215 +#define PS2_O_STROKE 216 +#define PS2_U_GRAVE 217 +#define PS2_U_ACUTE 218 +#define PS2_U_CIRCUMFLEX 219 +#define PS2_U_DIAERESIS 220 +#define PS2_Y_ACUTE 221 +#define PS2_THORN 222 +#define PS2_SHARP_S 223 +#define PS2_a_GRAVE 224 +#define PS2_a_ACUTE 225 +#define PS2_a_CIRCUMFLEX 226 +#define PS2_a_TILDE 227 +#define PS2_a_DIAERESIS 228 +#define PS2_a_RING_ABOVE 229 +#define PS2_ae 230 +#define PS2_c_CEDILLA 231 +#define PS2_e_GRAVE 232 +#define PS2_e_ACUTE 233 +#define PS2_e_CIRCUMFLEX 234 +#define PS2_e_DIAERESIS 235 +#define PS2_i_GRAVE 236 +#define PS2_i_ACUTE 237 +#define PS2_i_CIRCUMFLEX 238 +#define PS2_i_DIAERESIS 239 +#define PS2_eth 240 +#define PS2_n_TILDE 241 +#define PS2_o_GRAVE 242 +#define PS2_o_ACUTE 243 +#define PS2_o_CIRCUMFLEX 244 +#define PS2_o_TILDE 245 +#define PS2_o_DIAERESIS 246 +#define PS2_DIVISION 247 +#define PS2_o_STROKE 248 +#define PS2_u_GRAVE 249 +#define PS2_u_ACUTE 250 +#define PS2_u_CIRCUMFLEX 251 +#define PS2_u_DIAERESIS 252 +#define PS2_y_ACUTE 253 +#define PS2_thorn 254 +#define PS2_y_DIAERESIS 255 + +#define PS2_KEYMAP_SIZE 136 + +typedef struct { + uint8_t noshift[PS2_KEYMAP_SIZE]; + uint8_t shift[PS2_KEYMAP_SIZE]; + uint32_t uses_altgr; + uint8_t altgr[PS2_KEYMAP_SIZE]; +} ps2_keymap_t; + +/* US keyboard layout (default). */ +extern const ps2_keymap_t ps2_keymap_us; + +/** + * @brief Initialise the PS/2 keyboard driver. + * + * Configures the data and clock GPIO pins, installs the GPIO ISR service + * (if not already installed) and attaches the falling-edge interrupt on the + * clock pin. + * + * @param data_pin GPIO connected to the PS/2 DATA line. + * @param clock_pin GPIO connected to the PS/2 CLOCK line (interrupt source). + * @param keymap Keymap to use (e.g. &ps2_keymap_us). Pass NULL for default. + * + * @return ESP_OK on success, otherwise an esp_err_t code from the GPIO driver. + */ +esp_err_t ps2keyboard_begin(gpio_num_t data_pin, + gpio_num_t clock_pin, + const ps2_keymap_t *keymap); + +/** + * @brief Release ISR resources and reset internal state. + */ +void ps2keyboard_end(void); + +/** + * @brief Returns true if at least one decoded character is ready. + */ +bool ps2keyboard_available(void); + +/** + * @brief Discard any buffered character without returning it. + */ +void ps2keyboard_clear(void); + +/** + * @brief Pop one raw PS/2 scan code from the receive buffer. + * + * @return Scan code (0x00..0xFF), or 0 if the buffer is empty. + */ +uint8_t ps2keyboard_read_scancode(void); + +/** + * @brief Read the next decoded character, expanding non-ASCII to UTF-8. + * + * @return The byte read (0x00..0xFF), or -1 if no character is available. + */ +int ps2keyboard_read(void); + +/** + * @brief Read the next decoded character as a single Unicode code point. + * + * @return The code point (ISO-8859-1 range), or -1 if none available. + */ +int ps2keyboard_read_unicode(void); + +#ifdef __cplusplus +} +#endif + +#endif /* PS2KEYBOARD_H */ diff --git a/keywords.txt b/keywords.txt deleted file mode 100644 index bd2c18a..0000000 --- a/keywords.txt +++ /dev/null @@ -1,26 +0,0 @@ -####################################### -# Syntax Coloring Map For PS2Keyboard -####################################### - -####################################### -# Datatypes (KEYWORD1) -####################################### - -PS2Keyboard KEYWORD1 - -####################################### -# Methods and Functions (KEYWORD2) -####################################### - -####################################### -# Constants (LITERAL1) -####################################### - -PS2_KC_BREAK LITERAL1 -PS2_KC_ENTER LITERAL1 -PS2_KC_ESC LITERAL1 -PS2_KC_KPLUS LITERAL1 -PS2_KC_KMINUS LITERAL1 -PS2_KC_KMULTI LITERAL1 -PS2_KC_NUM LITERAL1 -PS2_KC_BKSP LITERAL1 diff --git a/library.json b/library.json deleted file mode 100644 index 77cc9c1..0000000 --- a/library.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "PS2Keyboard", - "keywords": "keyboard, emulation", - "description": "PS2Keyboard (PS/2) allows you to use a keyboard for user input", - "authors": - [ - { - "name": "Paul Stoffregen", - "email": "paul@pjrc.com", - "url": "http://www.pjrc.com", - "maintainer": true - }, - { - "name": "Christian Weichel", - "email": "info@32leaves.net" - }, - { - "name": "L. Abraham Smith", - "email": "n3bah@microcompdesign.com" - }, - { - "name": "Cuningan", - "email": "cuninganreset@gmail.com" - } - ], - "repository": - { - "type": "git", - "url": "https://github.com/PaulStoffregen/PS2Keyboard.git" - }, - "version": "2.4", - "homepage": "http://www.pjrc.com/teensy/td_libs_PS2Keyboard.html", - "frameworks": "arduino", - "platforms": - [ - "atmelavr", - "teensy" - ], - "examples": [ - "examples/*/*.pde" - ] -} diff --git a/library.properties b/library.properties deleted file mode 100644 index 340d0ba..0000000 --- a/library.properties +++ /dev/null @@ -1,10 +0,0 @@ -name=PS2Keyboard -version=2.4 -author=Christian Weichel, Paul Stoffregen, L. Abraham Smith, Cuningan -maintainer=Paul Stoffregen -sentence=Use a PS/2 Keyboard for input -paragraph= -category=Signal Input/Output -url=https://github.com/PaulStoffregen/PS2Keyboard -architectures=* - diff --git a/src/ps2keyboard.c b/src/ps2keyboard.c new file mode 100644 index 0000000..4976001 --- /dev/null +++ b/src/ps2keyboard.c @@ -0,0 +1,509 @@ +/* + ps2keyboard.c - PS/2 keyboard driver for ESP-IDF. + + Originally derived from the Arduino PS2Keyboard library by + Christian Weichel, Paul Stoffregen, L. Abraham Smith, Cuningan and + Lahiru Nirmal. Ported to native ESP-IDF, 2026. + + Released under the GNU Lesser General Public License v2.1. +*/ + +#include "ps2keyboard.h" + +#include + +#include "driver/gpio.h" +#include "esp_attr.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char *TAG = "ps2keyboard"; + +#define PS2_BUFFER_SIZE 45 +#define PS2_LED_CONTROL 0xED + +/* State machine flags for scan-code processing. */ +#define PS2_BREAK 0x01 +#define PS2_MODIFIER 0x02 +#define PS2_SHIFT_L 0x04 +#define PS2_SHIFT_R 0x08 +#define PS2_ALTGR 0x10 + +/* LED state bit flags (PS/2 LED command payload). */ +#define PS2_LED_SCROLL 0x01 +#define PS2_LED_NUM 0x02 +#define PS2_LED_CAPS 0x04 + +static volatile uint8_t s_buffer[PS2_BUFFER_SIZE]; +static volatile uint8_t s_head; +static volatile uint8_t s_tail; + +/* + ISR frame state lives at file scope (rather than function-local statics) + so that ps2_send_byte() can clear it after re-enabling the clock-line + interrupt. Otherwise a stale bit_count from before the bit-bang can + corrupt the first scan code that follows an LED update. +*/ +static volatile uint8_t s_isr_bit_count; +static volatile uint8_t s_isr_incoming; +static volatile uint32_t s_isr_prev_us; + +static gpio_num_t s_data_pin = GPIO_NUM_NC; +static gpio_num_t s_clock_pin = GPIO_NUM_NC; + +static const ps2_keymap_t *s_keymap; + +static uint8_t s_char_buffer; +static uint8_t s_utf8_next; + +static bool s_caps_lock_on; +static bool s_num_lock_on; +static bool s_scroll_lock_on; +static uint8_t s_led_state; + +static bool s_isr_service_installed; +static bool s_isr_handler_added; + +/* ---------- low-level pin helpers ---------- */ + +static inline int ps2_read_pin(gpio_num_t pin) +{ + return gpio_get_level(pin); +} + +static inline void ps2_write_pin(gpio_num_t pin, int level) +{ + gpio_set_level(pin, level); +} + +static inline void ps2_pin_input_pullup(gpio_num_t pin) +{ + gpio_set_direction(pin, GPIO_MODE_INPUT); + gpio_set_pull_mode(pin, GPIO_PULLUP_ONLY); +} + +static inline void ps2_pin_output(gpio_num_t pin) +{ + gpio_set_direction(pin, GPIO_MODE_OUTPUT); +} + +static void ps2_busy_wait_us(uint32_t us) +{ + int64_t start = esp_timer_get_time(); + while ((esp_timer_get_time() - start) < (int64_t)us) { + /* spin */ + } +} + +/* Spin-wait until `pin` reaches `target_level`, or until `timeout_us` elapses. + Returns true on success, false on timeout. */ +static bool ps2_wait_for_level(gpio_num_t pin, int target_level, uint32_t timeout_us) +{ + int64_t deadline = esp_timer_get_time() + (int64_t)timeout_us; + while (gpio_get_level(pin) != target_level) { + if (esp_timer_get_time() > deadline) { + return false; + } + } + return true; +} + +/* ---------- PS/2 command transmit (host -> keyboard) ---------- */ + +static bool ps2_parity_odd(uint16_t data) +{ + uint8_t count = 0; + for (int i = 0; i < 10; i++) { + count += (data >> i) & 1U; + } + return (count % 2U) != 0U; +} + +/* + Bit-bangs one byte (with parity + stop) to the keyboard. The clock-line + ISR is disabled for the duration so the device-driven clock doesn't + fire our scan-code handler with garbage. + + Reference: https://karooza.net/how-to-interface-a-ps2-keyboard +*/ +/* PS/2 host-to-device framing tops out around 70us per clock half-cycle. + 10ms per transition is comfortably generous and lets us bail out without + hanging the consumer task if the device stops clocking. */ +#define PS2_TX_PIN_TIMEOUT_US 10000U + +static esp_err_t ps2_send_byte(uint8_t cmd_byte) +{ + uint16_t cmd = cmd_byte; + if (!ps2_parity_odd(cmd)) { + cmd |= 0x0100; /* odd parity bit */ + } + cmd |= 0x0200; /* stop bit */ + + gpio_intr_disable(s_clock_pin); + + /* Pull clock low for ~100us to request transmission. */ + ps2_pin_output(s_clock_pin); + ps2_pin_output(s_data_pin); + ps2_write_pin(s_clock_pin, 0); + ps2_busy_wait_us(100); + + /* Start bit: pull data low, then release clock. */ + ps2_write_pin(s_data_pin, 0); + ps2_busy_wait_us(20); + ps2_pin_input_pullup(s_clock_pin); + + esp_err_t err = ESP_OK; + + /* 8 data bits + parity + stop bit. */ + for (int i = 0; i < 10; i++) { + if (!ps2_wait_for_level(s_clock_pin, 0, PS2_TX_PIN_TIMEOUT_US)) { + err = ESP_ERR_TIMEOUT; + goto out; + } + ps2_write_pin(s_data_pin, cmd & 0x0001); + cmd >>= 1; + if (!ps2_wait_for_level(s_clock_pin, 1, PS2_TX_PIN_TIMEOUT_US)) { + err = ESP_ERR_TIMEOUT; + goto out; + } + } + + /* Release data line for the device-driven ack. */ + ps2_pin_input_pullup(s_data_pin); + + /* Wait for keyboard ACK: clock low + data low. */ + if (!ps2_wait_for_level(s_clock_pin, 0, PS2_TX_PIN_TIMEOUT_US) || + !ps2_wait_for_level(s_data_pin, 0, PS2_TX_PIN_TIMEOUT_US)) { + err = ESP_ERR_TIMEOUT; + } + +out: + ps2_pin_input_pullup(s_clock_pin); + ps2_pin_input_pullup(s_data_pin); + + /* The device-driven clock cycles during this bit-bang were masked, so + any stale bit_count carried over would corrupt the next genuine scan + code. Reset before re-arming the ISR. */ + s_isr_bit_count = 0; + s_isr_incoming = 0; + s_isr_prev_us = (uint32_t)esp_timer_get_time(); + + gpio_intr_enable(s_clock_pin); + return err; +} + +static void ps2_update_leds(uint8_t leds) +{ + if (ps2_send_byte(PS2_LED_CONTROL) != ESP_OK) { + return; + } + (void)ps2_send_byte(leds); +} + +/* ---------- ISR: clock-line falling edge ---------- */ + +static void IRAM_ATTR ps2_isr_handler(void *arg) +{ + (void)arg; + + /* 32-bit time math is enough for the 250ms timeout and is cheaper on + the LX6 than the int64_t subtraction esp_timer_get_time() returns. + Wraparound (every ~71 minutes) is handled by unsigned subtraction. */ + uint32_t now_us = (uint32_t)esp_timer_get_time(); + int val = gpio_get_level(s_data_pin); + + if ((uint32_t)(now_us - s_isr_prev_us) > 250000U) { + s_isr_bit_count = 0; + s_isr_incoming = 0; + } + s_isr_prev_us = now_us; + + /* On the start-bit call, bit_count is 0 and (0 - 1) underflows to 0xFF + which fails the <=7 check. Subsequent calls land bits 0..7 into + incoming. */ + uint8_t bit_pos = (uint8_t)(s_isr_bit_count - 1); + if (bit_pos <= 7) { + s_isr_incoming |= (uint8_t)(val << bit_pos); + } + s_isr_bit_count++; + + if (s_isr_bit_count == 11) { + uint8_t i = (uint8_t)(s_head + 1); + if (i >= PS2_BUFFER_SIZE) { + i = 0; + } + if (i != s_tail) { + s_buffer[i] = s_isr_incoming; + s_head = i; + } + s_isr_bit_count = 0; + s_isr_incoming = 0; + } +} + +static inline uint8_t ps2_get_scancode(void) +{ + uint8_t i = s_tail; + if (i == s_head) { + return 0; + } + i++; + if (i >= PS2_BUFFER_SIZE) { + i = 0; + } + uint8_t c = s_buffer[i]; + s_tail = i; + return c; +} + +/* ---------- scan-code -> ISO-8859-1 decoder ---------- */ + +static uint8_t ps2_decode_iso8859(void) +{ + static uint8_t state = 0; + + /* Bail safely if a caller polled before ps2keyboard_begin() ran. */ + if (s_keymap == NULL) { + return 0; + } + + while (1) { + uint8_t scan = ps2_get_scancode(); + if (!scan) { + return 0; + } + + if (scan == 0xF0) { + state |= PS2_BREAK; + continue; + } + if (scan == 0xE0) { + state |= PS2_MODIFIER; + continue; + } + + if (state & PS2_BREAK) { + if (scan == 0x12) { + state &= ~PS2_SHIFT_L; + } else if (scan == 0x59) { + state &= ~PS2_SHIFT_R; + } else if (scan == 0x11 && (state & PS2_MODIFIER)) { + state &= ~PS2_ALTGR; + } + state &= ~(PS2_BREAK | PS2_MODIFIER); + continue; + } + + if (scan == 0x12) { + state |= PS2_SHIFT_L; + continue; + } + if (scan == 0x59) { + state |= PS2_SHIFT_R; + continue; + } + if (scan == 0x11 && (state & PS2_MODIFIER)) { + state |= PS2_ALTGR; + } else if (scan == 0x58) { + s_caps_lock_on = !s_caps_lock_on; + s_led_state = s_caps_lock_on + ? (s_led_state | PS2_LED_CAPS) + : (s_led_state & (uint8_t)~PS2_LED_CAPS); + ps2_update_leds(s_led_state); + } else if (scan == 0x77) { + s_num_lock_on = !s_num_lock_on; + s_led_state = s_num_lock_on + ? (s_led_state | PS2_LED_NUM) + : (s_led_state & (uint8_t)~PS2_LED_NUM); + ps2_update_leds(s_led_state); + } else if (scan == 0x7E) { + s_scroll_lock_on = !s_scroll_lock_on; + s_led_state = s_scroll_lock_on + ? (s_led_state | PS2_LED_SCROLL) + : (s_led_state & (uint8_t)~PS2_LED_SCROLL); + ps2_update_leds(s_led_state); + } + + uint8_t ch = 0; + if (state & PS2_MODIFIER) { + switch (scan) { + case 0x70: ch = PS2_INSERT; break; + case 0x6C: ch = PS2_HOME; break; + case 0x7D: ch = PS2_PAGEUP; break; + case 0x71: ch = PS2_DELETE; break; + case 0x69: ch = PS2_END; break; + case 0x7A: ch = PS2_PAGEDOWN; break; + case 0x75: ch = PS2_UPARROW; break; + case 0x6B: ch = PS2_LEFTARROW; break; + case 0x72: ch = PS2_DOWNARROW; break; + case 0x74: ch = PS2_RIGHTARROW; break; + case 0x4A: ch = '/'; break; + case 0x5A: ch = PS2_ENTER; break; + default: break; + } + } else if ((state & PS2_ALTGR) && s_keymap->uses_altgr) { + if (scan < PS2_KEYMAP_SIZE) { + ch = s_keymap->altgr[scan]; + } + } else if (scan < PS2_KEYMAP_SIZE) { + /* Caps Lock only inverts shift for alphabetic keys (a-z); for + everything else (digits, punctuation, function keys), Shift + is the sole modifier. Detect "letter" by inspecting the + unshifted entry rather than carrying a parallel flag table. */ + bool shift_active = (state & (PS2_SHIFT_L | PS2_SHIFT_R)) != 0; + uint8_t ns = s_keymap->noshift[scan]; + bool is_alpha = (ns >= 'a' && ns <= 'z'); + bool effective_shift = shift_active ^ (is_alpha && s_caps_lock_on); + ch = effective_shift ? s_keymap->shift[scan] : ns; + } + + state &= ~(PS2_BREAK | PS2_MODIFIER); + + if (ch) { + return ch; + } + } +} + +/* ---------- public API ---------- */ + +esp_err_t ps2keyboard_begin(gpio_num_t data_pin, + gpio_num_t clock_pin, + const ps2_keymap_t *keymap) +{ + if (!GPIO_IS_VALID_GPIO(data_pin) || !GPIO_IS_VALID_GPIO(clock_pin)) { + return ESP_ERR_INVALID_ARG; + } + + s_data_pin = data_pin; + s_clock_pin = clock_pin; + s_keymap = (keymap != NULL) ? keymap : &ps2_keymap_us; + s_head = 0; + s_tail = 0; + s_char_buffer = 0; + s_utf8_next = 0; + s_caps_lock_on = false; + s_num_lock_on = false; + s_scroll_lock_on = false; + s_led_state = 0; + s_isr_bit_count = 0; + s_isr_incoming = 0; + s_isr_prev_us = 0; + + gpio_config_t data_cfg = { + .pin_bit_mask = (1ULL << data_pin), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + esp_err_t err = gpio_config(&data_cfg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "gpio_config(data) failed: %s", esp_err_to_name(err)); + return err; + } + + gpio_config_t clk_cfg = { + .pin_bit_mask = (1ULL << clock_pin), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_NEGEDGE, + }; + err = gpio_config(&clk_cfg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "gpio_config(clock) failed: %s", esp_err_to_name(err)); + return err; + } + + if (!s_isr_service_installed) { + err = gpio_install_isr_service(ESP_INTR_FLAG_IRAM); + if (err == ESP_OK) { + s_isr_service_installed = true; + } else if (err != ESP_ERR_INVALID_STATE) { + ESP_LOGE(TAG, "gpio_install_isr_service failed: %s", esp_err_to_name(err)); + return err; + } + } + + err = gpio_isr_handler_add(clock_pin, ps2_isr_handler, NULL); + if (err != ESP_OK) { + ESP_LOGE(TAG, "gpio_isr_handler_add failed: %s", esp_err_to_name(err)); + return err; + } + s_isr_handler_added = true; + + return ESP_OK; +} + +void ps2keyboard_end(void) +{ + if (s_isr_handler_added && s_clock_pin != GPIO_NUM_NC) { + gpio_isr_handler_remove(s_clock_pin); + s_isr_handler_added = false; + } + s_char_buffer = 0; + s_utf8_next = 0; + s_head = 0; + s_tail = 0; +} + +bool ps2keyboard_available(void) +{ + if (s_char_buffer || s_utf8_next) { + return true; + } + s_char_buffer = (uint8_t)ps2_decode_iso8859(); + return s_char_buffer != 0; +} + +void ps2keyboard_clear(void) +{ + s_char_buffer = 0; + s_utf8_next = 0; +} + +uint8_t ps2keyboard_read_scancode(void) +{ + return ps2_get_scancode(); +} + +int ps2keyboard_read(void) +{ + uint8_t result = s_utf8_next; + if (result) { + s_utf8_next = 0; + } else { + result = s_char_buffer; + if (result) { + s_char_buffer = 0; + } else { + result = (uint8_t)ps2_decode_iso8859(); + } + if (result >= 128) { + s_utf8_next = (uint8_t)((result & 0x3F) | 0x80); + result = (uint8_t)(((result >> 6) & 0x1F) | 0xC0); + } + } + if (!result) { + return -1; + } + return result; +} + +int ps2keyboard_read_unicode(void) +{ + int result = s_char_buffer; + if (!result) { + result = (uint8_t)ps2_decode_iso8859(); + } + if (!result) { + return -1; + } + s_utf8_next = 0; + s_char_buffer = 0; + return result; +} diff --git a/src/ps2keymap_us.c b/src/ps2keymap_us.c new file mode 100644 index 0000000..fc01a82 --- /dev/null +++ b/src/ps2keymap_us.c @@ -0,0 +1,54 @@ +/* + ps2keymap_us.c - US keymap for the PS/2 keyboard ESP-IDF component. + + Scan code references: + http://www.quadibloc.com/comp/scan.htm + http://www.computer-engineering.org/ps2keyboard/scancodes2.html +*/ + +#include "ps2keyboard.h" + +const ps2_keymap_t ps2_keymap_us = { + /* without shift */ + { + 0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, + 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, '`', 0, + 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'q', '1', 0, + 0, 0, 'z', 's', 'a', 'w', '2', 0, + 0, 'c', 'x', 'd', 'e', '4', '3', 0, + 0, ' ', 'v', 'f', 't', 'r', '5', 0, + 0, 'n', 'b', 'h', 'g', 'y', '6', 0, + 0, 0, 'm', 'j', 'u', '7', '8', 0, + 0, ',', 'k', 'i', 'o', '0', '9', 0, + 0, '.', '/', 'l', ';', 'p', '-', 0, + 0, 0, '\'', 0, '[', '=', 0, 0, + 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER, ']', 0, '\\', 0, 0, + 0, 0, 0, 0, 0, 0, PS2_BACKSPACE, 0, + 0, '1', 0, '4', '7', 0, 0, 0, + '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, + PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, + 0, 0, 0, PS2_F7 + }, + /* with shift */ + { + 0, PS2_F9, 0, PS2_F5, PS2_F3, PS2_F1, PS2_F2, PS2_F12, + 0, PS2_F10, PS2_F8, PS2_F6, PS2_F4, PS2_TAB, '~', 0, + 0, 0 /*Lalt*/, 0 /*Lshift*/, 0, 0 /*Lctrl*/, 'Q', '!', 0, + 0, 0, 'Z', 'S', 'A', 'W', '@', 0, + 0, 'C', 'X', 'D', 'E', '$', '#', 0, + 0, ' ', 'V', 'F', 'T', 'R', '%', 0, + 0, 'N', 'B', 'H', 'G', 'Y', '^', 0, + 0, 0, 'M', 'J', 'U', '&', '*', 0, + 0, '<', 'K', 'I', 'O', ')', '(', 0, + 0, '>', '?', 'L', ':', 'P', '_', 0, + 0, 0, '"', 0, '{', '+', 0, 0, + 0 /*CapsLock*/, 0 /*Rshift*/, PS2_ENTER, '}', 0, '|', 0, 0, + 0, 0, 0, 0, 0, 0, PS2_BACKSPACE, 0, + 0, '1', 0, '4', '7', 0, 0, 0, + '0', '.', '2', '5', '6', '8', PS2_ESC, 0 /*NumLock*/, + PS2_F11, '+', '3', '-', '*', '9', PS2_SCROLL, 0, + 0, 0, 0, PS2_F7 + }, + 0, /* uses_altgr */ + { 0 }, +}; diff --git a/utility/int_pins.h b/utility/int_pins.h deleted file mode 100644 index 9621721..0000000 --- a/utility/int_pins.h +++ /dev/null @@ -1,80 +0,0 @@ -// interrupt pins for known boards - -// Teensy and maybe others automatically define this info -#if !defined(CORE_INT0_PIN) && !defined(CORE_INT1_PIN) && !defined(CORE_INT2_PIN)&& !defined(CORE_INT3_PIN) - -// Arduino Mega -#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) // Arduino Mega - #define CORE_INT0_PIN 2 - #define CORE_INT1_PIN 3 - #define CORE_INT2_PIN 21 - #define CORE_INT3_PIN 20 - #define CORE_INT4_PIN 19 - #define CORE_INT5_PIN 18 - -// Arduino Due (untested) -#elif defined(__SAM3X8E__) - #define CORE_INT_EVERY_PIN - #ifndef PROGMEM - #define PROGMEM - #endif - #ifndef pgm_read_byte - #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) - #endif - -// Arduino Leonardo (untested) -#elif defined(__AVR_ATmega32U4__) && !defined(CORE_TEENSY) - #define CORE_INT0_PIN 3 - #define CORE_INT1_PIN 2 - #define CORE_INT2_PIN 0 - #define CORE_INT3_PIN 1 - -// Sanguino (untested) -#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) // Sanguino - #define CORE_INT0_PIN 10 - #define CORE_INT1_PIN 11 - #define CORE_INT2_PIN 2 - -// Chipkit Uno32 (untested) -#elif defined(__PIC32MX__) && defined(_BOARD_UNO_) - #define CORE_INT0_PIN 38 - #define CORE_INT1_PIN 2 - #define CORE_INT2_PIN 7 - #define CORE_INT3_PIN 8 - #define CORE_INT4_PIN 35 - -// Chipkit Mega32 (untested) -#elif defined(__PIC32MX__) && defined(_BOARD_MEGA_) - #define CORE_INT0_PIN 3 - #define CORE_INT1_PIN 2 - #define CORE_INT2_PIN 7 - #define CORE_INT3_PIN 21 - #define CORE_INT4_PIN 20 - -// http://hlt.media.mit.edu/?p=1229 -#elif defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) - #define CORE_INT0_PIN 2 - -#elif defined(__AVR_ATtiny84__) - #define CORE_INT0_PIN 8 - -//ESP8266 - NodeMCU 0.9 (ESP-12 Module) / NodeMCU 1.0 (ESP-12E Module) -#elif defined(ESP8266) - #define CORE_INT_EVERY_PIN - -//ESP32 support -#elif defined(ESP32) - #define CORE_INT_EVERY_PIN - -//STM32F1 support -#elif defined(__STM32F1__) - #define CORE_INT_EVERY_PIN - -// Arduino Uno, Duemilanove, LilyPad, Mini, Fio, etc... -#else - #define CORE_INT0_PIN 2 - #define CORE_INT1_PIN 3 - -#endif -#endif -