summaryrefslogtreecommitdiff
path: root/code/esp32
diff options
context:
space:
mode:
Diffstat (limited to 'code/esp32')
-rw-r--r--code/esp32/LICENSE339
-rw-r--r--code/esp32/Makefile9
-rw-r--r--code/esp32/components/eos/at_cmd.c158
-rw-r--r--code/esp32/components/eos/bq25895.c60
-rw-r--r--code/esp32/components/eos/cell.c41
-rw-r--r--code/esp32/components/eos/cell_modem.c768
-rw-r--r--code/esp32/components/eos/cell_pcm.c258
-rw-r--r--code/esp32/components/eos/component.mk0
-rw-r--r--code/esp32/components/eos/drv2605l.c82
-rw-r--r--code/esp32/components/eos/i2c.c103
-rw-r--r--code/esp32/components/eos/include/at_cmd.h19
-rw-r--r--code/esp32/components/eos/include/bq25895.h3
-rw-r--r--code/esp32/components/eos/include/cell.h47
-rw-r--r--code/esp32/components/eos/include/drv2605l.h3
-rw-r--r--code/esp32/components/eos/include/eos.h23
-rw-r--r--code/esp32/components/eos/include/i2c.h9
-rw-r--r--code/esp32/components/eos/include/msgq.h32
-rw-r--r--code/esp32/components/eos/include/net.h34
-rw-r--r--code/esp32/components/eos/include/power.h20
-rw-r--r--code/esp32/components/eos/include/sock.h18
-rw-r--r--code/esp32/components/eos/include/wifi.h12
-rw-r--r--code/esp32/components/eos/msgq.c69
-rw-r--r--code/esp32/components/eos/net.c285
-rw-r--r--code/esp32/components/eos/power.c325
-rw-r--r--code/esp32/components/eos/sock.c163
-rwxr-xr-xcode/esp32/components/eos/wifi.c302
-rw-r--r--code/esp32/main/app_main.c38
-rw-r--r--code/esp32/main/component.mk8
28 files changed, 3228 insertions, 0 deletions
diff --git a/code/esp32/LICENSE b/code/esp32/LICENSE
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/code/esp32/LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, 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 or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) 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; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, 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 executable. However, as a
+special exception, the source code 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+ 5. 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 Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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 to
+this License.
+
+ 7. 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 Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), 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 Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/code/esp32/Makefile b/code/esp32/Makefile
new file mode 100644
index 0000000..eb26385
--- /dev/null
+++ b/code/esp32/Makefile
@@ -0,0 +1,9 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := eos-app
+
+include $(IDF_PATH)/make/project.mk
+
diff --git a/code/esp32/components/eos/at_cmd.c b/code/esp32/components/eos/at_cmd.c
new file mode 100644
index 0000000..ea31485
--- /dev/null
+++ b/code/esp32/components/eos/at_cmd.c
@@ -0,0 +1,158 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+#include <esp_log.h>
+
+#include "eos.h"
+
+#include "cell.h"
+#include "at_cmd.h"
+
+static const char *TAG = "EOS ATCMD";
+
+typedef struct ATURCItem {
+ regex_t re;
+ at_urc_cb_t cb;
+ char pattern[AT_SIZE_PATTERN];
+} ATURCItem;
+
+typedef struct ATURCList {
+ ATURCItem item[AT_SIZE_URC_LIST];
+ int len;
+} ATURCList;
+
+static ATURCList urc_list;
+static ATURCItem *urc_curr;
+static SemaphoreHandle_t mutex;
+
+static char at_buf[128];
+
+void at_init(void) {
+ memset(&urc_list, 0, sizeof(ATURCList));
+
+ mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(mutex);
+}
+
+int at_urc_process(char *urc) {
+ regmatch_t match[AT_SIZE_NMATCH];
+ at_urc_cb_t cb = NULL;
+ regmatch_t *m = NULL;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+
+ if (urc_curr == NULL) {
+ int i;
+
+ for (i=0; i<urc_list.len; i++) {
+ if (regexec(&urc_list.item[i].re, urc, AT_SIZE_NMATCH, match, 0) == 0) {
+ urc_curr = &urc_list.item[i];
+ m = match;
+ break;
+ }
+ }
+ }
+ if (urc_curr) cb = urc_curr->cb;
+
+ xSemaphoreGive(mutex);
+
+ if (cb) {
+ int r = cb(urc, m);
+
+ if (r != AT_URC_MORE) {
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ urc_curr = NULL;
+ xSemaphoreGive(mutex);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int at_urc_insert(char *pattern, at_urc_cb_t cb, int flags) {
+ int r;
+ int rv = EOS_OK;
+
+ if (strlen(pattern) >= AT_SIZE_PATTERN) return EOS_ERR;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+
+ r = regcomp(&urc_list.item[urc_list.len].re, pattern, flags);
+ if (r) rv = EOS_ERR;
+
+ if (!rv && (urc_list.len == AT_SIZE_URC_LIST)) rv = EOS_ERR_FULL;
+ if (!rv) {
+ strcpy(urc_list.item[urc_list.len].pattern, pattern);
+ urc_list.item[urc_list.len].cb = cb;
+ urc_list.len++;
+ }
+
+ xSemaphoreGive(mutex);
+
+ return rv;
+}
+
+int at_urc_delete(char *pattern) {
+ int i;
+ int rv = EOS_ERR_NOTFOUND;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+
+ for (i=0; i<urc_list.len; i++) {
+ if ((strcmp(pattern, urc_list.item[i].pattern) == 0)) {
+ if (i != urc_list.len - 1) memmove(&urc_list.item[i], &urc_list.item[i + 1], (urc_list.len - i - 1) * sizeof(ATURCItem));
+ urc_list.len--;
+ memset(&urc_list.item[urc_list.len], 0, sizeof(ATURCItem));
+ if (urc_curr) {
+ if (urc_curr == &urc_list.item[i]) {
+ urc_curr = NULL;
+ } else if (urc_curr > &urc_list.item[i]) {
+ urc_curr--;
+ }
+ }
+ rv = EOS_OK;
+ break;
+ }
+ }
+
+ xSemaphoreGive(mutex);
+
+ return rv;
+}
+
+int at_expect(char *str_ok, char *str_err, uint32_t timeout) {
+ int r;
+ int rv;
+ regex_t re_ok;
+ regex_t re_err;
+ uint32_t e = 0;
+ uint64_t t_start = esp_timer_get_time();
+
+ if (str_ok) {
+ rv = regcomp(&re_ok, str_ok, REG_EXTENDED | REG_NOSUB);
+ if (rv) return EOS_ERR;
+ }
+
+ if (str_err) {
+ rv = regcomp(&re_err, str_err, REG_EXTENDED | REG_NOSUB);
+ if (rv) return EOS_ERR;
+ }
+
+ do {
+ rv = eos_modem_readln(at_buf, sizeof(at_buf), timeout - e);
+ if (rv) return rv;
+
+ if (at_buf[0] == '\0') continue;
+
+ if (str_ok && (regexec(&re_ok, at_buf, 0, NULL, 0) == 0)) return 1;
+ if (str_err && (regexec(&re_err, at_buf, 0, NULL, 0) == 0)) return 0;
+
+ r = at_urc_process(at_buf);
+ if (!r) ESP_LOGD(TAG, "expect unhandled URC: %s", at_buf);
+
+ e = (uint32_t)(esp_timer_get_time() - t_start) / 1000;
+ if (e >= timeout) return EOS_ERR_TIMEOUT;
+ } while (1);
+}
diff --git a/code/esp32/components/eos/bq25895.c b/code/esp32/components/eos/bq25895.c
new file mode 100644
index 0000000..0d1bb8d
--- /dev/null
+++ b/code/esp32/components/eos/bq25895.c
@@ -0,0 +1,60 @@
+#include <stdlib.h>
+
+#include <esp_log.h>
+
+#include "eos.h"
+#include "i2c.h"
+
+static const char *TAG = "EOS BQ25895";
+
+#define BQ25895_ADDR 0x6A
+
+void eos_bq25895_set_ilim(void) {
+ uint8_t data = 0;
+ eos_i2c_write8(BQ25895_ADDR, 0, 0x1c);
+ eos_i2c_write8(BQ25895_ADDR, 2, 0x28);
+ eos_i2c_write8(BQ25895_ADDR, 7, 0x8d);
+
+ data = eos_i2c_read8(BQ25895_ADDR, 0x00);
+ ESP_LOGI(TAG, "REG00: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x01);
+ ESP_LOGI(TAG, "REG01: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x02);
+ ESP_LOGI(TAG, "REG02: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x03);
+ ESP_LOGI(TAG, "REG03: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x04);
+ ESP_LOGI(TAG, "REG04: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x05);
+ ESP_LOGI(TAG, "REG05: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x06);
+ ESP_LOGI(TAG, "REG06: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x07);
+ ESP_LOGI(TAG, "REG07: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x08);
+ ESP_LOGI(TAG, "REG08: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x09);
+ ESP_LOGI(TAG, "REG09: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x0a);
+ ESP_LOGI(TAG, "REG0A: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x0b);
+ ESP_LOGI(TAG, "REG0B: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x0c);
+ ESP_LOGI(TAG, "REG0C: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x0d);
+ ESP_LOGI(TAG, "REG0D: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x0e);
+ ESP_LOGI(TAG, "REG0E: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x0f);
+ ESP_LOGI(TAG, "REG0F: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x10);
+ ESP_LOGI(TAG, "REG10: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x11);
+ ESP_LOGI(TAG, "REG11: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x12);
+ ESP_LOGI(TAG, "REG12: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x13);
+ ESP_LOGI(TAG, "REG13: %02x", data);
+ data = eos_i2c_read8(BQ25895_ADDR, 0x14);
+ ESP_LOGI(TAG, "REG14: %02x", data);
+}
diff --git a/code/esp32/components/eos/cell.c b/code/esp32/components/eos/cell.c
new file mode 100644
index 0000000..2da1450
--- /dev/null
+++ b/code/esp32/components/eos/cell.c
@@ -0,0 +1,41 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <esp_log.h>
+
+#include "eos.h"
+#include "net.h"
+#include "cell.h"
+
+static uint8_t cell_mode;
+
+static void cell_handler(unsigned char _mtype, unsigned char *buffer, uint16_t size) {
+ uint8_t mtype = buffer[0];
+
+ switch (mtype) {
+ case EOS_CELL_MTYPE_DATA:
+ if (eos_modem_get_mode() == EOS_CELL_UART_MODE_RELAY) eos_modem_write(buffer+1, size-1);
+ break;
+ case EOS_CELL_MTYPE_DATA_START:
+ cell_mode = eos_modem_get_mode();
+ eos_modem_set_mode(EOS_CELL_UART_MODE_RELAY);
+ break;
+ case EOS_CELL_MTYPE_DATA_STOP:
+ eos_modem_set_mode(cell_mode);
+ break;
+ case EOS_CELL_MTYPE_AUDIO:
+ eos_pcm_push(buffer+1, size-1);
+ break;
+ case EOS_CELL_MTYPE_AUDIO_START:
+ eos_pcm_start();
+ break;
+ case EOS_CELL_MTYPE_AUDIO_STOP:
+ eos_pcm_stop();
+ break;
+ }
+}
+
+void eos_cell_init(void) {
+ eos_net_set_handler(EOS_NET_MTYPE_CELL, cell_handler);
+}
+
diff --git a/code/esp32/components/eos/cell_modem.c b/code/esp32/components/eos/cell_modem.c
new file mode 100644
index 0000000..5315861
--- /dev/null
+++ b/code/esp32/components/eos/cell_modem.c
@@ -0,0 +1,768 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+#include <freertos/task.h>
+#include <freertos/queue.h>
+#include <netif/ppp/pppos.h>
+#include <netif/ppp/pppapi.h>
+#include <driver/uart.h>
+#include <driver/gpio.h>
+#include <esp_sleep.h>
+#include <esp_log.h>
+
+#include "eos.h"
+#include "net.h"
+#include "power.h"
+
+#include "at_cmd.h"
+#include "cell.h"
+
+// XXX: PPP reconnect on failure
+
+#define UART_SIZE_BUF 1024
+#define UART_SIZE_URC_BUF 128
+
+#define UART_GPIO_TXD 16
+#define UART_GPIO_RXD 17
+#define UART_GPIO_DTR 32
+#define UART_GPIO_RI 35
+
+#define MODEM_ETYPE_INIT 1
+#define MODEM_ETYPE_RI 2
+
+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
+#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
+
+static const char *TAG = "EOS MODEM";
+
+static SemaphoreHandle_t mutex;
+
+static QueueHandle_t modem_queue;
+static QueueHandle_t uart_queue;
+
+static char uart_buf[UART_SIZE_URC_BUF];
+static unsigned int uart_buf_len;
+
+static uint8_t uart_mode = EOS_CELL_UART_MODE_ATCMD;
+static SemaphoreHandle_t uart_mutex;
+
+static char ppp_apn[64];
+static char ppp_user[64];
+static char ppp_pass[64];
+static SemaphoreHandle_t ppp_mutex;
+
+static ppp_pcb *ppp_handle;
+static struct netif ppp_netif;
+
+typedef enum {
+ UART_EEVT_MODE = UART_EVENT_MAX
+} uart_eevt_type_t;
+
+typedef struct {
+ uint8_t type;
+} modem_event_t;
+
+static void modem_atcmd_read(size_t bsize);
+
+static void uart_data_read(uint8_t mode) {
+ unsigned char *buf;
+ int rd;
+ size_t bsize;
+
+ uart_get_buffered_data_len(UART_NUM_2, &bsize);
+ switch (mode) {
+ case EOS_CELL_UART_MODE_ATCMD:
+ modem_atcmd_read(bsize);
+ break;
+
+ case EOS_CELL_UART_MODE_PPP:
+ rd = 0;
+
+ do {
+ int _rd = eos_modem_read(uart_buf, MIN(bsize - rd, sizeof(uart_buf)), 100);
+ pppos_input_tcpip(ppp_handle, (uint8_t *)uart_buf, _rd);
+ rd += _rd;
+ } while (rd != bsize);
+ break;
+
+ case EOS_CELL_UART_MODE_RELAY:
+ rd = 0;
+
+ do {
+ int _rd;
+
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_DATA;
+ _rd = eos_modem_read(buf + 1, MIN(bsize - rd, EOS_NET_SIZE_BUF - 1), 100);
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, _rd + 1, 0);
+ rd += _rd;
+ } while (rd != bsize);
+ break;
+
+ default:
+ break;
+
+ }
+}
+
+static void uart_event_task(void *pvParameters) {
+ char mode = EOS_CELL_UART_MODE_ATCMD;
+ char _mode = EOS_CELL_UART_MODE_ATCMD;
+ uart_event_t event;
+
+ xSemaphoreTake(uart_mutex, portMAX_DELAY);
+ while (1) {
+ /* Waiting for UART event.
+ */
+ if (xQueueReceive(uart_queue, &event, portMAX_DELAY)) {
+ switch (event.type) {
+ case UART_DATA:
+ /* Event of UART receiving data
+ */
+ if (mode != EOS_CELL_UART_MODE_NONE) uart_data_read(mode);
+ if ((mode != _mode) && (uart_buf_len == 0)) {
+ if (_mode == EOS_CELL_UART_MODE_NONE) xSemaphoreGive(uart_mutex);
+ mode = _mode;
+ }
+ break;
+
+ case UART_EEVT_MODE:
+ /* Mode change
+ */
+ _mode = (char)event.size;
+ if ((_mode != mode) && ((uart_buf_len == 0) || (mode == EOS_CELL_UART_MODE_NONE))) {
+ if (mode == EOS_CELL_UART_MODE_NONE) {
+ xSemaphoreTake(uart_mutex, portMAX_DELAY);
+ uart_data_read(_mode);
+ }
+ if (_mode == EOS_CELL_UART_MODE_NONE) xSemaphoreGive(uart_mutex);
+ mode = _mode;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ vTaskDelete(NULL);
+}
+
+static void IRAM_ATTR uart_ri_isr_handler(void *arg) {
+ modem_event_t evt;
+
+ evt.type = MODEM_ETYPE_RI;
+ xQueueSendFromISR(modem_queue, &evt, NULL);
+}
+
+static int modem_atcmd_init(void) {
+ unsigned char *buf;
+ int echo_on = 0;
+ int tries = 3;
+ int r;
+ int rv = EOS_OK;
+
+ rv = eos_modem_take(1000);
+ if (rv) return rv;
+
+ do {
+ eos_modem_write("AT\r", 3);
+ r = at_expect("^AT", "^OK", 1000);
+ if (r >= 0) {
+ echo_on = r;
+ if (echo_on) {
+ r = at_expect("^OK", NULL, 1000);
+ }
+ break;
+ }
+ tries--;
+ } while (tries);
+
+ if (tries == 0) {
+ eos_modem_give();
+ return EOS_ERR_TIMEOUT;
+ }
+
+ if (echo_on) {
+ eos_modem_write("AT&F\r", 5);
+ r = at_expect("^AT&F", NULL, 1000);
+ r = at_expect("^OK", NULL, 1000);
+ } else {
+ r = eos_modem_write("AT&F\r", 5);
+ r = at_expect("^OK", NULL, 1000);
+
+ }
+ eos_modem_write("ATE0\r", 5);
+ r = at_expect("^ATE0", NULL, 1000);
+ r = at_expect("^OK", "^ERROR", 1000);
+
+ eos_modem_write("AT+CFGRI=1\r", 11);
+ r = at_expect("^OK", "^ERROR", 1000);
+ eos_modem_write("AT+CSCLK=1\r", 11);
+ r = at_expect("^OK", "^ERROR", 1000);
+
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_READY;
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, 1, 0);
+
+ eos_modem_give();
+
+ return EOS_OK;
+}
+
+static void modem_atcmd_read(size_t bsize) {
+ char *ln_begin, *ln_end, *ln_next;
+ int rd = 0;
+
+ do {
+ int _rd = eos_modem_read(uart_buf + uart_buf_len, MIN(bsize - rd, sizeof(uart_buf) - uart_buf_len - 1), 100);
+ ln_next = uart_buf + uart_buf_len;
+ ln_begin = uart_buf;
+ uart_buf_len += _rd;
+ rd += _rd;
+ uart_buf[uart_buf_len] = '\0';
+ while ((ln_end = strchr(ln_next, '\n'))) {
+ ln_end--;
+ if ((ln_end > ln_begin) && (*ln_end == '\r')) {
+ int r;
+
+ *ln_end = '\0';
+ r = at_urc_process(ln_begin);
+ if (!r) {
+ ESP_LOGD(TAG, "unhandled URC: %s", ln_begin);
+ }
+ }
+ ln_next = ln_end + 2;
+ ln_begin = ln_next;
+ }
+ if (ln_begin != uart_buf) {
+ uart_buf_len -= ln_begin - uart_buf;
+ if (uart_buf_len) memmove(uart_buf, ln_begin, uart_buf_len);
+ } else if (uart_buf_len == sizeof(uart_buf) - 1) {
+ memcpy(uart_buf, uart_buf + sizeof(uart_buf) / 2, sizeof(uart_buf) / 2);
+ uart_buf_len = sizeof(uart_buf) / 2;
+ }
+ } while (rd != bsize);
+}
+
+int modem_urc_init_handler(char *urc, regmatch_t *m) {
+ modem_event_t evt;
+
+ evt.type = MODEM_ETYPE_INIT;
+ xQueueSend(modem_queue, &evt, portMAX_DELAY);
+
+ return AT_URC_OK;
+}
+
+static void modem_set_mode(uint8_t mode) {
+ uart_event_t evt;
+
+ evt.type = UART_EEVT_MODE;
+ evt.size = mode;
+ xQueueSend(uart_queue, &evt, portMAX_DELAY);
+}
+
+static void modem_event_task(void *pvParameters) {
+ modem_event_t evt;
+
+ while (1) {
+ if (xQueueReceive(modem_queue, &evt, portMAX_DELAY)) {
+ switch (evt.type) {
+ case MODEM_ETYPE_INIT:
+ modem_atcmd_init();
+ break;
+
+ case MODEM_ETYPE_RI:
+ ESP_LOGI(TAG, "URC from RI");
+ break;
+
+ default:
+ break;
+ }
+
+ /* Obsolete!!!
+ uint64_t t_start = esp_timer_get_time();
+ if (xQueueReceive(modem_queue, &level, 200 / portTICK_RATE_MS) && (level == 1)) {
+ uint64_t t_end = esp_timer_get_time();
+ ESP_LOGI(TAG, "URC:%u", (uint32_t)(t_end - t_start));
+ } else {
+ ESP_LOGI(TAG, "RING");
+ }
+ */
+
+ }
+ }
+ vTaskDelete(NULL);
+}
+
+static char *memstr(char *mem, size_t size, char *str) {
+ size_t i = 0;
+ char *max_mem;
+
+ if (str[0] == '\0') return NULL;
+
+ max_mem = mem + size;
+
+ while (mem < max_mem) {
+ if (*mem != str[i]) {
+ mem -= i;
+ i = 0;
+ } else {
+ if (str[i+1] == '\0') return mem - i;
+ i++;
+ }
+ mem++;
+ }
+
+ return NULL;
+}
+
+static uint32_t ppp_output_cb(ppp_pcb *pcb, uint8_t *data, uint32_t len, void *ctx) {
+ size_t rv;
+
+ xSemaphoreTake(ppp_mutex, portMAX_DELAY);
+ rv = eos_modem_write(data, len);
+ xSemaphoreGive(ppp_mutex);
+
+ return rv;
+}
+
+/* PPP status callback */
+static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
+ // struct netif *pppif = ppp_netif(pcb);
+ // LWIP_UNUSED_ARG(ctx);
+
+ switch(err_code) {
+ case PPPERR_NONE: {
+ ESP_LOGE(TAG, "status_cb: Connect");
+ break;
+ }
+ case PPPERR_PARAM: {
+ ESP_LOGE(TAG, "status_cb: Invalid parameter");
+ break;
+ }
+ case PPPERR_OPEN: {
+ ESP_LOGE(TAG, "status_cb: Unable to open PPP session");
+ break;
+ }
+ case PPPERR_DEVICE: {
+ ESP_LOGE(TAG, "status_cb: Invalid I/O device for PPP");
+ break;
+ }
+ case PPPERR_ALLOC: {
+ ESP_LOGE(TAG, "status_cb: Unable to allocate resources");
+ break;
+ }
+ case PPPERR_USER: {
+ ESP_LOGE(TAG, "status_cb: User interrupt");
+ break;
+ }
+ case PPPERR_CONNECT: {
+ ESP_LOGE(TAG, "status_cb: Connection lost");
+ break;
+ }
+ case PPPERR_AUTHFAIL: {
+ ESP_LOGE(TAG, "status_cb: Failed authentication challenge");
+ break;
+ }
+ case PPPERR_PROTOCOL: {
+ ESP_LOGE(TAG, "status_cb: Failed to meet protocol");
+ break;
+ }
+ case PPPERR_PEERDEAD: {
+ ESP_LOGE(TAG, "status_cb: Connection timeout");
+ break;
+ }
+ case PPPERR_IDLETIMEOUT: {
+ ESP_LOGE(TAG, "status_cb: Idle Timeout");
+ break;
+ }
+ case PPPERR_CONNECTTIME: {
+ ESP_LOGE(TAG, "status_cb: Max connect time reached");
+ break;
+ }
+ case PPPERR_LOOPBACK: {
+ ESP_LOGE(TAG, "status_cb: Loopback detected");
+ break;
+ }
+ default: {
+ ESP_LOGE(TAG, "status_cb: Unknown error code %d", err_code);
+ break;
+ }
+ }
+}
+
+static int ppp_pause(uint32_t timeout, uint8_t retries) {
+ int done = 0;
+ int len = 0;
+ int rv = EOS_OK;
+ char *ok_str = NULL;
+ uint64_t t_start;
+
+ timeout += 1000;
+ xSemaphoreTake(ppp_mutex, portMAX_DELAY);
+ eos_modem_flush();
+ vTaskDelay(1000 / portTICK_PERIOD_MS);
+ modem_set_mode(EOS_CELL_UART_MODE_NONE);
+ xSemaphoreTake(uart_mutex, portMAX_DELAY);
+ eos_modem_write("+++", 3);
+ t_start = esp_timer_get_time();
+
+ do {
+ len = eos_modem_read(uart_buf + uart_buf_len, sizeof(uart_buf) - uart_buf_len, 10);
+ if (len > 0) {
+ if (uart_buf_len > 5) {
+ ok_str = memstr(uart_buf + uart_buf_len - 5, len + 5, "\r\nOK\r\n");
+ } else {
+ ok_str = memstr(uart_buf, len + uart_buf_len, "\r\nOK\r\n");
+ }
+ uart_buf_len += len;
+ }
+ if (ok_str) {
+ pppos_input_tcpip(ppp_handle, (uint8_t *)uart_buf, ok_str - uart_buf);
+ ok_str += 6;
+ uart_buf_len -= ok_str - uart_buf;
+ if (uart_buf_len) memmove(uart_buf, ok_str, uart_buf_len);
+ done = 1;
+ } else if (uart_buf_len == sizeof(uart_buf)) {
+ pppos_input_tcpip(ppp_handle, (uint8_t *)uart_buf, sizeof(uart_buf) / 2);
+ memcpy(uart_buf, uart_buf + sizeof(uart_buf) / 2, sizeof(uart_buf) / 2);
+ uart_buf_len = sizeof(uart_buf) / 2;
+ }
+ if (timeout && !done && ((uint32_t)((esp_timer_get_time() - t_start) / 1000) > timeout)) {
+ if (!retries) {
+ modem_set_mode(EOS_CELL_UART_MODE_PPP);
+ xSemaphoreGive(uart_mutex);
+ xSemaphoreGive(ppp_mutex);
+ rv = EOS_ERR_TIMEOUT;
+ done = 1;
+ } else {
+ retries--;
+ eos_modem_write("+++", 3);
+ t_start = esp_timer_get_time();
+ }
+ }
+ } while (!done);
+
+ return rv;
+}
+
+static int ppp_resume(void) {
+ int r;
+ int rv = EOS_OK;
+
+ eos_modem_write("ATO\r", 4);
+ r = at_expect("^CONNECT", "^(ERROR|NO CARRIER)", 1000);
+ if (r <= 0) rv = EOS_ERR;
+
+ modem_set_mode(EOS_CELL_UART_MODE_PPP);
+ xSemaphoreGive(uart_mutex);
+ xSemaphoreGive(ppp_mutex);
+
+ return rv;
+}
+
+static int ppp_setup(void) {
+ int r;
+ char cmd[64];
+ int cmd_len = snprintf(cmd, sizeof(cmd), "AT+CGDCONT=1,\"IP\",\"%s\"\r", ppp_apn);
+
+ if ((cmd_len < 0) || (cmd_len >= sizeof(cmd))) return EOS_ERR;
+
+ modem_set_mode(EOS_CELL_UART_MODE_NONE);
+ r = xSemaphoreTake(uart_mutex, 1000 / portTICK_PERIOD_MS);
+ if (r == pdFALSE) {
+ modem_set_mode(uart_mode);
+ return EOS_ERR_TIMEOUT;
+ }
+
+ eos_modem_write(cmd, cmd_len);
+ r = at_expect("^OK", "^ERROR", 1000);
+ if (r <= 0) {
+ modem_set_mode(uart_mode);
+ xSemaphoreGive(uart_mutex);
+ return EOS_ERR;
+ }
+
+ eos_modem_write("AT+CGDATA=\"PPP\",1\r", 18);
+ r = at_expect("^CONNECT", "^(ERROR|\\+CME ERROR|NO CARRIER)", 1000);
+ if (r <= 0) {
+ modem_set_mode(uart_mode);
+ xSemaphoreGive(uart_mutex);
+ return EOS_ERR;
+ }
+
+ ppp_handle = pppapi_pppos_create(&ppp_netif, ppp_output_cb, ppp_status_cb, NULL);
+ ppp_set_usepeerdns(ppp_handle, 1);
+ pppapi_set_default(ppp_handle);
+ pppapi_set_auth(ppp_handle, PPPAUTHTYPE_PAP, ppp_user, ppp_pass);
+ pppapi_connect(ppp_handle, 0);
+
+ modem_set_mode(EOS_CELL_UART_MODE_PPP);
+ xSemaphoreGive(uart_mutex);
+
+ return EOS_OK;
+}
+
+static int ppp_disconnect(void) {
+ int rv;
+
+ pppapi_close(ppp_handle, 0);
+
+ rv = ppp_pause(1000, 2);
+ if (rv) return rv;
+
+ eos_modem_write("ATH\r", 4);
+ at_expect("^OK", NULL, 1000);
+
+ xSemaphoreGive(uart_mutex);
+ xSemaphoreGive(ppp_mutex);
+ ppp_handle = NULL;
+
+ return EOS_OK;
+}
+
+void eos_modem_init(void) {
+ /* Configure parameters of an UART driver,
+ * communication pins and install the driver */
+ uart_config_t uart_config = {
+ .baud_rate = 115200,
+ .data_bits = UART_DATA_8_BITS,
+ .parity = UART_PARITY_DISABLE,
+ .stop_bits = UART_STOP_BITS_1,
+ .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
+ };
+ uart_param_config(UART_NUM_2, &uart_config);
+ uart_set_pin(UART_NUM_2, UART_GPIO_TXD, UART_GPIO_RXD, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
+ uart_driver_install(UART_NUM_2, UART_SIZE_BUF, UART_SIZE_BUF, 10, &uart_queue, 0);
+
+ // Configuration for the DTR/RI lines
+ gpio_config_t io_conf;
+
+ io_conf.intr_type = GPIO_INTR_DISABLE;
+ io_conf.mode = GPIO_MODE_OUTPUT;
+ io_conf.pin_bit_mask = ((uint64_t)1 << UART_GPIO_DTR);
+ io_conf.pull_up_en = 0;
+ io_conf.pull_down_en = 0;
+ gpio_config(&io_conf);
+ gpio_set_level(UART_GPIO_DTR, 0);
+
+ io_conf.intr_type = GPIO_INTR_NEGEDGE;
+ io_conf.mode = GPIO_MODE_INPUT;
+ io_conf.pin_bit_mask = ((uint64_t)1 << UART_GPIO_RI);
+ io_conf.pull_up_en = 0;
+ io_conf.pull_down_en = 0;
+ gpio_config(&io_conf);
+
+ mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(mutex);
+
+ uart_mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(uart_mutex);
+
+ ppp_mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(ppp_mutex);
+
+ modem_queue = xQueueCreate(4, sizeof(modem_event_t));
+ xTaskCreate(uart_event_task, "uart_event", EOS_TASK_SSIZE_UART, NULL, EOS_TASK_PRIORITY_UART, NULL);
+ xTaskCreate(modem_event_task, "modem_event", EOS_TASK_SSIZE_MODEM, NULL, EOS_TASK_PRIORITY_MODEM, NULL);
+
+ gpio_isr_handler_add(UART_GPIO_RI, uart_ri_isr_handler, NULL);
+
+ at_init();
+ at_urc_insert("^PB DONE", modem_urc_init_handler, REG_EXTENDED);
+ eos_modem_set_mode(EOS_CELL_UART_MODE_ATCMD);
+
+ ESP_LOGI(TAG, "INIT");
+}
+
+void eos_modem_flush(void){
+ uart_wait_tx_done(UART_NUM_2, portMAX_DELAY);
+}
+
+size_t eos_modem_write(void *data, size_t size) {
+ return uart_write_bytes(UART_NUM_2, (const char *)data, size);
+}
+
+size_t eos_modem_read(void *data, size_t size, uint32_t timeout) {
+ return uart_read_bytes(UART_NUM_2, (uint8_t *)data, size, timeout / portTICK_RATE_MS);
+}
+
+int eos_modem_readln(char *buf, size_t buf_size, uint32_t timeout) {
+ int done = 0;
+ int len = 0;
+ int rv = EOS_OK;
+ char *ln_end = NULL;
+ uint64_t t_start = esp_timer_get_time();
+
+ uart_buf[uart_buf_len] = '\0';
+ ln_end = strchr(uart_buf, '\n');
+
+ do {
+ if (ln_end == NULL) {
+ len = eos_modem_read(uart_buf + uart_buf_len, sizeof(uart_buf) - uart_buf_len - 1, 10);
+ if (len > 0) {
+ uart_buf[uart_buf_len + len] = '\0';
+ ln_end = strchr(uart_buf + uart_buf_len, '\n');
+ uart_buf_len += len;
+ }
+ }
+ if (ln_end) {
+ ln_end--;
+ if ((ln_end >= uart_buf) && (*ln_end == '\r')) {
+ if (buf) {
+ if (buf_size > ln_end - uart_buf) {
+ memcpy(buf, uart_buf, ln_end - uart_buf);
+ buf[ln_end - uart_buf] = '\0';
+ } else {
+ rv = EOS_ERR;
+ }
+ }
+ done = 1;
+ }
+ ln_end += 2;
+ uart_buf_len -= ln_end - uart_buf;
+ if (uart_buf_len) memmove(uart_buf, ln_end, uart_buf_len);
+ ln_end = NULL;
+ } else if (uart_buf_len == sizeof(uart_buf) - 1) {
+ memcpy(uart_buf, uart_buf + sizeof(uart_buf) / 2, sizeof(uart_buf) / 2);
+ uart_buf_len = sizeof(uart_buf) / 2;
+ }
+ if (timeout && !done && ((uint32_t)((esp_timer_get_time() - t_start) / 1000) > timeout)) {
+ rv = EOS_ERR_TIMEOUT;
+ done = 1;
+ }
+ } while (!done);
+
+ return rv;
+}
+
+uint8_t eos_modem_get_mode(void) {
+ uint8_t ret;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ ret = uart_mode;
+ xSemaphoreGive(mutex);
+
+ return ret;
+}
+
+int eos_modem_set_mode(uint8_t mode) {
+ int rv = EOS_OK;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (mode != uart_mode) {
+ if (uart_mode == EOS_CELL_UART_MODE_PPP) rv = ppp_disconnect();
+ if (!rv) {
+ if (mode == EOS_CELL_UART_MODE_PPP) {
+ rv = ppp_setup();
+ } else {
+ modem_set_mode(mode);
+ }
+ if (!rv) uart_mode = mode;
+ }
+ }
+ xSemaphoreGive(mutex);
+
+ return rv;
+}
+
+int eos_modem_take(uint32_t timeout) {
+ int rv = EOS_OK;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (uart_mode == EOS_CELL_UART_MODE_PPP) {
+ rv = ppp_pause(timeout, 0);
+ } else {
+ int r;
+
+ modem_set_mode(EOS_CELL_UART_MODE_NONE);
+ r = xSemaphoreTake(uart_mutex, timeout ? timeout / portTICK_PERIOD_MS : portMAX_DELAY);
+ if (r == pdFALSE) {
+ modem_set_mode(uart_mode);
+ rv = EOS_ERR_TIMEOUT;
+ }
+ }
+
+ if (rv) xSemaphoreGive(mutex);
+
+ return rv;
+}
+
+void eos_modem_give(void) {
+ if (uart_mode == EOS_CELL_UART_MODE_PPP) {
+ int rv = ppp_resume();
+ if (rv) ESP_LOGW(TAG, "PPP resume failed");
+ } else {
+ modem_set_mode(uart_mode);
+ xSemaphoreGive(uart_mutex);
+ }
+ xSemaphoreGive(mutex);
+}
+
+void eos_modem_sleep(uint8_t mode) {
+ int r;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ modem_set_mode(EOS_CELL_UART_MODE_NONE);
+ r = xSemaphoreTake(uart_mutex, 1000 / portTICK_PERIOD_MS);
+ if (r == pdFALSE) {
+ ESP_LOGE(TAG, "Obtaining mutex before sleep failed");
+ }
+ gpio_set_level(UART_GPIO_DTR, 1);
+ if (mode == EOS_PWR_SMODE_DEEP) {
+ gpio_hold_en(UART_GPIO_DTR);
+ }
+}
+
+void eos_modem_wake(uint8_t source, uint8_t mode) {
+ if (source == EOS_PWR_WAKE_UART) {
+ modem_event_t evt;
+
+ evt.type = MODEM_ETYPE_RI;
+ xQueueSend(modem_queue, &evt, portMAX_DELAY);
+ }
+
+ if (mode != EOS_PWR_SMODE_DEEP) {
+ gpio_set_intr_type(UART_GPIO_RI, GPIO_INTR_NEGEDGE);
+ gpio_isr_handler_add(UART_GPIO_RI, uart_ri_isr_handler, NULL);
+ gpio_set_level(UART_GPIO_DTR, 0);
+
+ modem_set_mode(uart_mode);
+ xSemaphoreGive(uart_mutex);
+ xSemaphoreGive(mutex);
+ } else {
+ gpio_hold_dis(UART_GPIO_DTR);
+ }
+}
+
+void eos_ppp_set_apn(char *apn) {
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ strncpy(ppp_apn, apn, sizeof(ppp_apn) - 1);
+ xSemaphoreGive(mutex);
+}
+
+void eos_ppp_set_auth(char *user, char *pass) {
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ strncpy(ppp_user, user, sizeof(ppp_user) - 1);
+ strncpy(ppp_pass, pass, sizeof(ppp_pass) - 1);
+ xSemaphoreGive(mutex);
+}
+
+int eos_ppp_connect(void) {
+ return eos_modem_set_mode(EOS_CELL_UART_MODE_PPP);
+}
+
+int eos_ppp_disconnect(void) {
+ int rv = eos_modem_set_mode(EOS_CELL_UART_MODE_ATCMD);
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ memset(ppp_apn, 0, sizeof(ppp_apn));
+ memset(ppp_user, 0, sizeof(ppp_user));
+ memset(ppp_pass, 0, sizeof(ppp_pass));
+ xSemaphoreGive(mutex);
+
+ return rv;
+} \ No newline at end of file
diff --git a/code/esp32/components/eos/cell_pcm.c b/code/esp32/components/eos/cell_pcm.c
new file mode 100644
index 0000000..c1419b8
--- /dev/null
+++ b/code/esp32/components/eos/cell_pcm.c
@@ -0,0 +1,258 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+#include <freertos/task.h>
+#include <freertos/queue.h>
+#include <driver/i2s.h>
+#include <driver/gpio.h>
+#include <esp_log.h>
+
+#include "eos.h"
+#include "net.h"
+#include "msgq.h"
+#include "cell.h"
+
+#define PCM_MIC_WM 128
+#define PCM_HOLD_CNT_TX 3
+#define PCM_HOLD_CNT_RX 3
+#define PCM_SIZE_BUFQ 4
+#define PCM_SIZE_BUF (PCM_MIC_WM * 4)
+
+#define PCM_GPIO_BCK 33
+#define PCM_GPIO_WS 4
+#define PCM_GPIO_DIN 34
+#define PCM_GPIO_DOUT 2
+
+#define PCM_ETYPE_WRITE 1
+
+static EOSBufQ pcm_buf_q;
+static unsigned char *pcm_bufq_array[PCM_SIZE_BUFQ];
+
+static EOSMsgQ pcm_evt_q;
+static EOSMsgItem pcm_evtq_array[PCM_SIZE_BUFQ];
+static char pcm_hold_tx;
+
+static i2s_dev_t* I2S[I2S_NUM_MAX] = {&I2S0, &I2S1};
+
+static QueueHandle_t i2s_queue;
+static SemaphoreHandle_t mutex;
+
+static const char *TAG = "EOS PCM";
+
+static void i2s_event_task(void *pvParameters) {
+ i2s_event_t event;
+ unsigned char *buf;
+ unsigned char _type;
+ size_t bytes_w;
+ ssize_t bytes_r;
+ uint16_t bytes_e;
+ ssize_t hold_bytes_r = 0;
+ unsigned char *hold_buf = NULL;
+ char hold_cnt = 0;
+
+ while (1) {
+ // Waiting for I2S event.
+ if (xQueueReceive(i2s_queue, &event, portMAX_DELAY)) {
+ switch (event.type) {
+ case I2S_EVENT_RX_DONE:
+ // Event of I2S receiving data
+ if (!hold_cnt) {
+ buf = eos_net_alloc();
+ buf[0] = EOS_CELL_MTYPE_AUDIO;
+ bytes_r = eos_pcm_read(buf + 1, PCM_MIC_WM);
+ eos_net_send(EOS_NET_MTYPE_CELL, buf, bytes_r + 1, 0);
+ } else {
+ hold_cnt--;
+ if (hold_buf == NULL) {
+ hold_buf = eos_net_alloc();
+ hold_buf[0] = EOS_CELL_MTYPE_AUDIO;
+ }
+ if (1 + hold_bytes_r + PCM_MIC_WM <= EOS_NET_SIZE_BUF) hold_bytes_r += eos_pcm_read(hold_buf + 1 + hold_bytes_r, PCM_MIC_WM);
+ if (hold_cnt == 0) {
+ eos_net_send(EOS_NET_MTYPE_CELL, hold_buf, hold_bytes_r + 1, 0);
+ hold_bytes_r = 0;
+ hold_buf = NULL;
+ }
+ }
+
+ buf = NULL;
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (pcm_hold_tx && (eos_msgq_len(&pcm_evt_q) == PCM_HOLD_CNT_TX)) pcm_hold_tx = 0;
+ if (!pcm_hold_tx) eos_msgq_pop(&pcm_evt_q, &_type, &buf, &bytes_e, NULL);
+ xSemaphoreGive(mutex);
+
+ if (buf) {
+ i2s_write(I2S_NUM_0, (const void *)buf, bytes_e, &bytes_w, portMAX_DELAY);
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ eos_bufq_push(&pcm_buf_q, buf);
+ xSemaphoreGive(mutex);
+ }
+ break;
+ case I2S_EVENT_DMA_ERROR:
+ ESP_LOGE(TAG, "*** I2S DMA ERROR ***");
+ break;
+ case I2S_EVENT_MAX:
+ hold_cnt = PCM_HOLD_CNT_RX;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ vTaskDelete(NULL);
+}
+
+void eos_pcm_init(void) {
+ int i;
+
+ i2s_config_t i2s_config = {
+ .mode = I2S_MODE_SLAVE | I2S_MODE_TX | I2S_MODE_RX,
+ .sample_rate = 32000,
+ .bits_per_sample = 32,
+ .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
+ .communication_format = I2S_COMM_FORMAT_I2S,
+ .dma_buf_count = 4,
+ .dma_buf_len = PCM_MIC_WM,
+ .use_apll = true,
+ .fixed_mclk = 2048000 * 8,
+ .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1
+ };
+
+ i2s_pin_config_t pin_config = {
+ .bck_io_num = PCM_GPIO_BCK,
+ .ws_io_num = PCM_GPIO_WS,
+ .data_in_num = PCM_GPIO_DIN,
+ .data_out_num = PCM_GPIO_DOUT
+ };
+ i2s_driver_install(I2S_NUM_0, &i2s_config, 10, &i2s_queue); //install and start i2s driver
+ i2s_stop(I2S_NUM_0);
+ i2s_set_pin(I2S_NUM_0, &pin_config);
+ gpio_matrix_in(pin_config.ws_io_num, I2S0I_WS_IN_IDX, 1);
+ gpio_matrix_in(pin_config.bck_io_num, I2S0I_BCK_IN_IDX, 1);
+ ESP_LOGI(TAG, "TX FIFO:%d TX CHAN:%d RX FIFO:%d RX CHAN:%d", I2S[I2S_NUM_0]->fifo_conf.tx_fifo_mod, I2S[I2S_NUM_0]->conf_chan.tx_chan_mod, I2S[I2S_NUM_0]->fifo_conf.rx_fifo_mod, I2S[I2S_NUM_0]->conf_chan.rx_chan_mod);
+
+ I2S[I2S_NUM_0]->fifo_conf.tx_fifo_mod = 2;
+ I2S[I2S_NUM_0]->conf_chan.tx_chan_mod = 0;
+
+ I2S[I2S_NUM_0]->fifo_conf.rx_fifo_mod = 3;
+ I2S[I2S_NUM_0]->conf_chan.rx_chan_mod = 1;
+ // I2S[I2S_NUM_0]->conf.tx_mono = 1;
+ I2S[I2S_NUM_0]->conf.rx_mono = 1;
+ // I2S[I2S_NUM_0]->timing.tx_dsync_sw = 1
+ // I2S[I2S_NUM_0]->timing.rx_dsync_sw = 1
+ // I2S[I2S_NUM_0]->conf.sig_loopback = 0;
+
+ // I2S[I2S_NUM_0]->timing.tx_bck_in_inv = 1;
+
+ eos_msgq_init(&pcm_evt_q, pcm_evtq_array, PCM_SIZE_BUFQ);
+ eos_bufq_init(&pcm_buf_q, pcm_bufq_array, PCM_SIZE_BUFQ);
+ for (i=0; i<PCM_SIZE_BUFQ; i++) {
+ eos_bufq_push(&pcm_buf_q, malloc(PCM_SIZE_BUF));
+ }
+
+ mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(mutex);
+
+ // Create a task to handle i2s event from ISR
+ xTaskCreate(i2s_event_task, "i2s_event", EOS_TASK_SSIZE_I2S, NULL, EOS_TASK_PRIORITY_I2S, NULL);
+ ESP_LOGI(TAG, "INIT");
+}
+
+ssize_t eos_pcm_read(unsigned char *data, size_t size) {
+ static unsigned char buf[PCM_SIZE_BUF];
+ size_t bytes_r;
+ int i;
+
+ if (size > PCM_MIC_WM) return EOS_ERR;
+
+ esp_err_t ret = i2s_read(I2S_NUM_0, (void *)buf, size * 4, &bytes_r, portMAX_DELAY);
+ if (ret != ESP_OK) return EOS_ERR;
+
+ for (i=0; i<size/2; i++) {
+ data[i * 2] = buf[i * 8 + 3];
+ data[i * 2 + 1] = buf[i * 8 + 2];
+ }
+
+ return bytes_r / 4;
+}
+
+ssize_t eos_pcm_expand(unsigned char *buf, unsigned char *data, size_t size) {
+ int i;
+
+ if (size > PCM_MIC_WM) return EOS_ERR;
+
+ memset(buf, 0, PCM_SIZE_BUF);
+ for (i=0; i<size/2; i++) {
+ buf[i * 8 + 3] = data[i * 2];
+ buf[i * 8 + 2] = data[i * 2 + 1];
+ }
+
+ return size * 4;
+}
+
+int eos_pcm_push(unsigned char *data, size_t size) {
+ unsigned char *buf = NULL;
+ ssize_t esize;
+ int rv;
+
+ if (size > PCM_MIC_WM) return EOS_ERR;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (pcm_hold_tx && (eos_msgq_len(&pcm_evt_q) == PCM_HOLD_CNT_TX)) {
+ unsigned char _type;
+ uint16_t _len;
+
+ eos_msgq_pop(&pcm_evt_q, &_type, &buf, &_len, NULL);
+ } else {
+ buf = eos_bufq_pop(&pcm_buf_q);
+ }
+ xSemaphoreGive(mutex);
+
+ if (buf == NULL) return EOS_ERR_EMPTY;
+
+ esize = eos_pcm_expand(buf, data, size);
+ if (esize < 0) {
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ eos_bufq_push(&pcm_buf_q, buf);
+ xSemaphoreGive(mutex);
+ return esize;
+ }
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ rv = eos_msgq_push(&pcm_evt_q, PCM_ETYPE_WRITE, buf, esize, 0);
+ if (rv) eos_bufq_push(&pcm_buf_q, buf);
+ xSemaphoreGive(mutex);
+
+ return rv;
+}
+
+void eos_pcm_start(void) {
+ i2s_event_t evt;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ while (1) {
+ unsigned char _type;
+ unsigned char *buf;
+ uint16_t len;
+
+ eos_msgq_pop(&pcm_evt_q, &_type, &buf, &len, NULL);
+ if (buf) {
+ eos_bufq_push(&pcm_buf_q, buf);
+ } else {
+ break;
+ }
+ }
+ pcm_hold_tx = 1;
+ xSemaphoreGive(mutex);
+
+ evt.type = I2S_EVENT_MAX; /* my type */
+ xQueueSend(i2s_queue, &evt, portMAX_DELAY);
+ i2s_zero_dma_buffer(I2S_NUM_0);
+ i2s_start(I2S_NUM_0);
+}
+
+void eos_pcm_stop(void) {
+ i2s_stop(I2S_NUM_0);
+}
diff --git a/code/esp32/components/eos/component.mk b/code/esp32/components/eos/component.mk
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/code/esp32/components/eos/component.mk
diff --git a/code/esp32/components/eos/drv2605l.c b/code/esp32/components/eos/drv2605l.c
new file mode 100644
index 0000000..3944289
--- /dev/null
+++ b/code/esp32/components/eos/drv2605l.c
@@ -0,0 +1,82 @@
+#include <stdlib.h>
+
+#include <esp_log.h>
+
+#include "eos.h"
+#include "i2c.h"
+
+static const char *TAG = "EOS DRV2605L";
+
+#define DRV2605L_ADDR 0x5A
+
+#define DRV2605_REG_STATUS 0x00 ///< Status register
+#define DRV2605_REG_MODE 0x01 ///< Mode register
+#define DRV2605_MODE_INTTRIG 0x00 ///< Internal trigger mode
+#define DRV2605_MODE_EXTTRIGEDGE 0x01 ///< External edge trigger mode
+#define DRV2605_MODE_EXTTRIGLVL 0x02 ///< External level trigger mode
+#define DRV2605_MODE_PWMANALOG 0x03 ///< PWM/Analog input mode
+#define DRV2605_MODE_AUDIOVIBE 0x04 ///< Audio-to-vibe mode
+#define DRV2605_MODE_REALTIME 0x05 ///< Real-time playback (RTP) mode
+#define DRV2605_MODE_DIAGNOS 0x06 ///< Diagnostics mode
+#define DRV2605_MODE_AUTOCAL 0x07 ///< Auto calibration mode
+
+#define DRV2605_REG_RTPIN 0x02 ///< Real-time playback input register
+#define DRV2605_REG_LIBRARY 0x03 ///< Waveform library selection register
+#define DRV2605_REG_WAVESEQ1 0x04 ///< Waveform sequence register 1
+#define DRV2605_REG_WAVESEQ2 0x05 ///< Waveform sequence register 2
+#define DRV2605_REG_WAVESEQ3 0x06 ///< Waveform sequence register 3
+#define DRV2605_REG_WAVESEQ4 0x07 ///< Waveform sequence register 4
+#define DRV2605_REG_WAVESEQ5 0x08 ///< Waveform sequence register 5
+#define DRV2605_REG_WAVESEQ6 0x09 ///< Waveform sequence register 6
+#define DRV2605_REG_WAVESEQ7 0x0A ///< Waveform sequence register 7
+#define DRV2605_REG_WAVESEQ8 0x0B ///< Waveform sequence register 8
+
+#define DRV2605_REG_GO 0x0C ///< Go register
+#define DRV2605_REG_OVERDRIVE 0x0D ///< Overdrive time offset register
+#define DRV2605_REG_SUSTAINPOS 0x0E ///< Sustain time offset, positive register
+#define DRV2605_REG_SUSTAINNEG 0x0F ///< Sustain time offset, negative register
+#define DRV2605_REG_BREAK 0x10 ///< Brake time offset register
+#define DRV2605_REG_AUDIOCTRL 0x11 ///< Audio-to-vibe control register
+#define DRV2605_REG_AUDIOLVL 0x12 ///< Audio-to-vibe minimum input level register
+#define DRV2605_REG_AUDIOMAX 0x13 ///< Audio-to-vibe maximum input level register
+#define DRV2605_REG_AUDIOOUTMIN 0x14 ///< Audio-to-vibe minimum output drive register
+#define DRV2605_REG_AUDIOOUTMAX 0x15 ///< Audio-to-vibe maximum output drive register
+#define DRV2605_REG_RATEDV 0x16 ///< Rated voltage register
+#define DRV2605_REG_CLAMPV 0x17 ///< Overdrive clamp voltage register
+#define DRV2605_REG_AUTOCALCOMP 0x18 ///< Auto-calibration compensation result register
+#define DRV2605_REG_AUTOCALEMP 0x19 ///< Auto-calibration back-EMF result register
+#define DRV2605_REG_FEEDBACK 0x1A ///< Feedback control register
+#define DRV2605_REG_CONTROL1 0x1B ///< Control1 Register
+#define DRV2605_REG_CONTROL2 0x1C ///< Control2 Register
+#define DRV2605_REG_CONTROL3 0x1D ///< Control3 Register
+#define DRV2605_REG_CONTROL4 0x1E ///< Control4 Register
+#define DRV2605_REG_VBAT 0x21 ///< Vbat voltage-monitor register
+#define DRV2605_REG_LRARESON 0x22 ///< LRA resonance-period register
+
+void eos_drv2605l_test(void) {
+ uint8_t data = 0;
+
+ int ret = eos_i2c_read(DRV2605L_ADDR, DRV2605_REG_STATUS, &data, 1);
+ if (ret) ESP_LOGE(TAG, "I2C ERROR!");
+
+ eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_MODE, 0x00); // out of standby
+ eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_RTPIN, 0x00); // no real-time-playback
+
+ eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_WAVESEQ1, 1); // strong click
+ eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_WAVESEQ2, 0); // end sequence
+
+ eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_OVERDRIVE, 0); // no overdrive
+
+ eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_SUSTAINPOS, 0);
+ eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_SUSTAINNEG, 0);
+ eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_BREAK, 0);
+ eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_AUDIOMAX, 0x64);
+
+ // LRA open loop
+ eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_FEEDBACK, eos_i2c_read8(DRV2605L_ADDR, DRV2605_REG_FEEDBACK) | 0x80); // turn on N_ERM_LRA
+ eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_CONTROL3, eos_i2c_read8(DRV2605L_ADDR, DRV2605_REG_CONTROL3) | 0x01); // turn on LRA_OPEN_LOOP
+
+ eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_LIBRARY, 6); // set LRA library
+ eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_GO, 1); // go
+
+}
diff --git a/code/esp32/components/eos/i2c.c b/code/esp32/components/eos/i2c.c
new file mode 100644
index 0000000..5b8fcc7
--- /dev/null
+++ b/code/esp32/components/eos/i2c.c
@@ -0,0 +1,103 @@
+#include <stdlib.h>
+
+#include <esp_log.h>
+#include <driver/i2c.h>
+
+#include "eos.h"
+
+static const char *TAG = "EOS I2C";
+
+#define I2C_MASTER_NUM I2C_NUM_0
+#define I2C_MASTER_FREQ_HZ 100000
+#define I2C_MASTER_GPIO_SCL 25
+#define I2C_MASTER_GPIO_SDA 26
+
+#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
+#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
+#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
+#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
+#define ACK_VAL 0x0 /*!< I2C ack value */
+#define NCK_VAL 0x1 /*!< I2C nack value */
+
+/**
+ * @brief i2c initialization
+ */
+
+void eos_i2c_init(void) {
+ i2c_config_t conf;
+ conf.mode = I2C_MODE_MASTER;
+ conf.sda_io_num = I2C_MASTER_GPIO_SDA;
+ conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
+ conf.scl_io_num = I2C_MASTER_GPIO_SCL;
+ conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
+ conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
+ i2c_param_config(I2C_MASTER_NUM, &conf);
+ i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
+ ESP_LOGI(TAG, "INIT");
+}
+
+/**
+ * @brief i2c read
+ */
+
+int eos_i2c_read(uint8_t addr, uint8_t reg, uint8_t *data, size_t len) {
+ int i, ret;
+ i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+ i2c_master_start(cmd);
+ i2c_master_write_byte(cmd, addr << 1 | I2C_MASTER_WRITE, ACK_CHECK_EN);
+ i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
+ i2c_master_start(cmd);
+ i2c_master_write_byte(cmd, addr << 1 | I2C_MASTER_READ, ACK_CHECK_EN);
+ for (i=0; i < len - 1; i++) {
+ i2c_master_read_byte(cmd, data+i, ACK_VAL);
+ }
+ i2c_master_read_byte(cmd, data+i, NCK_VAL);
+ i2c_master_stop(cmd);
+ ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
+ i2c_cmd_link_delete(cmd);
+ if (ret != ESP_OK) {
+ return EOS_ERR;
+ }
+ return EOS_OK;
+}
+
+/**
+ * @brief i2c read8
+ */
+
+uint8_t eos_i2c_read8(uint8_t addr, uint8_t reg) {
+ uint8_t data;
+ eos_i2c_read(addr, reg, &data, 1);
+ return data;
+}
+
+/**
+ * @brief i2c write
+ */
+
+int eos_i2c_write(uint8_t addr, uint8_t reg, uint8_t *data, size_t len) {
+ int i, ret;
+ i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+ i2c_master_start(cmd);
+ i2c_master_write_byte(cmd, addr << 1 | I2C_MASTER_WRITE, ACK_CHECK_EN);
+ i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
+ for (i=0; i < len; i++) {
+ i2c_master_write_byte(cmd, *(data+i), ACK_CHECK_EN);
+ }
+ i2c_master_stop(cmd);
+ ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
+ i2c_cmd_link_delete(cmd);
+ if (ret != ESP_OK) {
+ return EOS_ERR;
+ }
+ return EOS_OK;
+}
+
+/**
+ * @brief i2c write8
+ */
+
+void eos_i2c_write8(uint8_t addr, uint8_t reg, uint8_t data) {
+ eos_i2c_write(addr, reg, &data, 1);
+}
+
diff --git a/code/esp32/components/eos/include/at_cmd.h b/code/esp32/components/eos/include/at_cmd.h
new file mode 100644
index 0000000..ca46e23
--- /dev/null
+++ b/code/esp32/components/eos/include/at_cmd.h
@@ -0,0 +1,19 @@
+#include <sys/types.h>
+#include <stdint.h>
+#include <regex.h>
+
+#define AT_SIZE_NMATCH 4
+#define AT_SIZE_PATTERN 64
+
+#define AT_SIZE_URC_LIST 16
+
+#define AT_URC_OK 0
+#define AT_URC_MORE 1
+
+typedef int (*at_urc_cb_t) (char *, regmatch_t[]);
+
+void at_init(void);
+int at_urc_process(char *urc);
+int at_urc_insert(char *pattern, at_urc_cb_t cb, int flags);
+int at_urc_delete(char *pattern);
+int at_expect(char *str_ok, char *str_err, uint32_t timeout); \ No newline at end of file
diff --git a/code/esp32/components/eos/include/bq25895.h b/code/esp32/components/eos/include/bq25895.h
new file mode 100644
index 0000000..b5a7f92
--- /dev/null
+++ b/code/esp32/components/eos/include/bq25895.h
@@ -0,0 +1,3 @@
+#include <stdint.h>
+
+void eos_bq25895_set_ilim(void); \ No newline at end of file
diff --git a/code/esp32/components/eos/include/cell.h b/code/esp32/components/eos/include/cell.h
new file mode 100644
index 0000000..23adecf
--- /dev/null
+++ b/code/esp32/components/eos/include/cell.h
@@ -0,0 +1,47 @@
+#include <sys/types.h>
+
+#define EOS_CELL_MTYPE_READY 0
+#define EOS_CELL_MTYPE_DATA 1
+#define EOS_CELL_MTYPE_AUDIO 2
+
+#define EOS_CELL_MTYPE_DATA_START 4
+#define EOS_CELL_MTYPE_DATA_STOP 5
+
+#define EOS_CELL_MTYPE_AUDIO_START 6
+#define EOS_CELL_MTYPE_AUDIO_STOP 7
+
+#define EOS_CELL_UART_MODE_NONE 0
+#define EOS_CELL_UART_MODE_ATCMD 1
+#define EOS_CELL_UART_MODE_PPP 2
+#define EOS_CELL_UART_MODE_RELAY 3
+
+void eos_pcm_init(void);
+
+ssize_t eos_pcm_read(unsigned char *data, size_t size);
+int eos_pcm_push(unsigned char *data, size_t size);
+void eos_pcm_start(void);
+void eos_pcm_stop(void);
+
+void eos_modem_init(void);
+
+void eos_modem_flush(void);
+size_t eos_modem_write(void *data, size_t size);
+size_t eos_modem_read(void *data, size_t size, uint32_t timeout);
+int eos_modem_readln(char *buf, size_t buf_size, uint32_t timeout);
+int eos_modem_resp(char *ok_str, char *err_str, uint32_t timeout);
+
+uint8_t eos_modem_get_mode(void);
+int eos_modem_set_mode(uint8_t mode);
+int eos_modem_take(uint32_t timeout);
+void eos_modem_give(void);
+
+void eos_modem_sleep(uint8_t mode);
+void eos_modem_wake(uint8_t source, uint8_t mode);
+
+void eos_ppp_set_apn(char *apn);
+void eos_ppp_set_auth(char *user, char *pass);
+
+int eos_ppp_connect(void);
+int eos_ppp_disconnect(void);
+
+void eos_cell_init(void);
diff --git a/code/esp32/components/eos/include/drv2605l.h b/code/esp32/components/eos/include/drv2605l.h
new file mode 100644
index 0000000..de222e4
--- /dev/null
+++ b/code/esp32/components/eos/include/drv2605l.h
@@ -0,0 +1,3 @@
+#include <stdint.h>
+
+void eos_drv2605l_test(void); \ No newline at end of file
diff --git a/code/esp32/components/eos/include/eos.h b/code/esp32/components/eos/include/eos.h
new file mode 100644
index 0000000..0e660fb
--- /dev/null
+++ b/code/esp32/components/eos/include/eos.h
@@ -0,0 +1,23 @@
+#define EOS_OK 0
+#define EOS_ERR -1
+#define EOS_ERR_TIMEOUT -2
+#define EOS_ERR_BUSY -3
+
+#define EOS_ERR_FULL -10
+#define EOS_ERR_EMPTY -11
+#define EOS_ERR_NOTFOUND -12
+
+#define EOS_TASK_PRIORITY_UART 1
+#define EOS_TASK_PRIORITY_MODEM 1
+#define EOS_TASK_PRIORITY_I2S 1
+#define EOS_TASK_PRIORITY_NET_XCHG 1
+#define EOS_TASK_PRIORITY_UDP_RCVR 1
+#define EOS_TASK_PRIORITY_PWR 1
+
+#define EOS_TASK_SSIZE_UART 4096
+#define EOS_TASK_SSIZE_MODEM 4096
+#define EOS_TASK_SSIZE_I2S 4096
+#define EOS_TASK_SSIZE_NET_XCHG 8192
+#define EOS_TASK_SSIZE_UDP_RCVR 4096
+#define EOS_TASK_SSIZE_PWR 4096
+
diff --git a/code/esp32/components/eos/include/i2c.h b/code/esp32/components/eos/include/i2c.h
new file mode 100644
index 0000000..144f5e1
--- /dev/null
+++ b/code/esp32/components/eos/include/i2c.h
@@ -0,0 +1,9 @@
+#include <sys/types.h>
+#include <stdint.h>
+
+void eos_i2c_init(void);
+
+int eos_i2c_read(uint8_t addr, uint8_t reg, uint8_t *data, size_t len);
+uint8_t eos_i2c_read8(uint8_t addr, uint8_t reg);
+int eos_i2c_write(uint8_t addr, uint8_t reg, uint8_t *data, size_t len);
+void eos_i2c_write8(uint8_t addr, uint8_t reg, uint8_t data);
diff --git a/code/esp32/components/eos/include/msgq.h b/code/esp32/components/eos/include/msgq.h
new file mode 100644
index 0000000..86bb067
--- /dev/null
+++ b/code/esp32/components/eos/include/msgq.h
@@ -0,0 +1,32 @@
+#include <stdint.h>
+
+typedef struct EOSMsgItem {
+ unsigned char type;
+ unsigned char *buffer;
+ uint16_t len;
+ uint8_t flags;
+} EOSMsgItem;
+
+typedef struct EOSMsgQ {
+ uint8_t idx_r;
+ uint8_t idx_w;
+ uint8_t size;
+ EOSMsgItem *array;
+} EOSMsgQ;
+
+void eos_msgq_init(EOSMsgQ *msgq, EOSMsgItem *array, uint8_t size);
+int eos_msgq_push(EOSMsgQ *msgq, unsigned char type, unsigned char *buffer, uint16_t len, uint8_t flags);
+void eos_msgq_pop(EOSMsgQ *msgq, unsigned char *type, unsigned char **buffer, uint16_t *len, uint8_t *flags);
+uint8_t eos_msgq_len(EOSMsgQ *msgq);
+
+typedef struct EOSBufQ {
+ uint8_t idx_r;
+ uint8_t idx_w;
+ uint8_t size;
+ unsigned char **array;
+} EOSBufQ;
+
+void eos_bufq_init(EOSBufQ *bufq, unsigned char **array, uint8_t size);
+int eos_bufq_push(EOSBufQ *bufq, unsigned char *buffer);
+unsigned char *eos_bufq_pop(EOSBufQ *bufq);
+uint8_t eos_bufq_len(EOSBufQ *bufq);
diff --git a/code/esp32/components/eos/include/net.h b/code/esp32/components/eos/include/net.h
new file mode 100644
index 0000000..54bad6d
--- /dev/null
+++ b/code/esp32/components/eos/include/net.h
@@ -0,0 +1,34 @@
+#include <stdint.h>
+
+/* common */
+#define EOS_NET_SIZE_BUF 1500
+
+#define EOS_NET_MTYPE_SOCK 1
+#define EOS_NET_MTYPE_POWER 4
+
+#define EOS_NET_MTYPE_WIFI 5
+#define EOS_NET_MTYPE_CELL 6
+#define EOS_NET_MTYPE_SIP 7
+#define EOS_NET_MTYPE_APP 8
+
+#define EOS_NET_MAX_MTYPE 8
+
+#define EOS_NET_MTYPE_FLAG_ONEW 0x80
+
+/* esp32 specific */
+#define EOS_NET_SIZE_BUFQ 4
+#define EOS_NET_SIZE_SNDQ 4
+
+#define EOS_NET_FLAG_BFREE 0x1
+#define EOS_NET_FLAG_BCOPY 0x2
+
+typedef void (*eos_net_fptr_t) (unsigned char, unsigned char *, uint16_t);
+
+void eos_net_init(void);
+
+unsigned char *eos_net_alloc(void);
+void eos_net_free(unsigned char *buf);
+int eos_net_send(unsigned char mtype, unsigned char *buffer, uint16_t len, uint8_t flags);
+void eos_net_set_handler(unsigned char mtype, eos_net_fptr_t handler);
+void eos_net_sleep_done(uint8_t mode);
+void eos_net_wake(uint8_t source, uint8_t mode);
diff --git a/code/esp32/components/eos/include/power.h b/code/esp32/components/eos/include/power.h
new file mode 100644
index 0000000..0a57b19
--- /dev/null
+++ b/code/esp32/components/eos/include/power.h
@@ -0,0 +1,20 @@
+#include <stdint.h>
+
+#define EOS_PWR_MTYPE_BUTTON 0
+
+#define EOS_PWR_WAKE_RST 0
+#define EOS_PWR_WAKE_BTN 1
+#define EOS_PWR_WAKE_NET 2
+#define EOS_PWR_WAKE_MSG 3
+#define EOS_PWR_WAKE_UART 4
+
+#define EOS_PWR_SMODE_LIGHT 1
+#define EOS_PWR_SMODE_DEEP 2
+
+void eos_power_init(void);
+
+void eos_power_wait4init(void);
+uint8_t eos_power_wakeup_cause(void);
+void eos_power_sleep(void);
+void eos_power_wake(uint8_t source);
+void eos_power_net_ready(void); \ No newline at end of file
diff --git a/code/esp32/components/eos/include/sock.h b/code/esp32/components/eos/include/sock.h
new file mode 100644
index 0000000..7e937cb
--- /dev/null
+++ b/code/esp32/components/eos/include/sock.h
@@ -0,0 +1,18 @@
+#include <stdint.h>
+
+#define EOS_SOCK_MTYPE_PKT 0
+#define EOS_SOCK_MTYPE_OPEN_DGRAM 1
+#define EOS_SOCK_MTYPE_CLOSE 127
+
+#define EOS_SOCK_MAX_SOCK 2
+
+#define EOS_SOCK_SIZE_UDP_HDR 8
+
+#define EOS_IPv4_ADDR_SIZE 4
+
+typedef struct EOSNetAddr {
+ unsigned char host[EOS_IPv4_ADDR_SIZE];
+ uint16_t port;
+} EOSNetAddr;
+
+void eos_sock_init(void); \ No newline at end of file
diff --git a/code/esp32/components/eos/include/wifi.h b/code/esp32/components/eos/include/wifi.h
new file mode 100644
index 0000000..6009f7c
--- /dev/null
+++ b/code/esp32/components/eos/include/wifi.h
@@ -0,0 +1,12 @@
+#define EOS_WIFI_MTYPE_SCAN 0
+#define EOS_WIFI_MTYPE_CONNECT 1
+#define EOS_WIFI_MTYPE_DISCONNECT 2
+
+#define EOS_WIFI_MAX_MTYPE 3
+
+void eos_wifi_init(void);
+
+int eos_wifi_scan(void);
+int eos_wifi_set_auth(char *ssid, char *pass);
+int eos_wifi_connect(void);
+int eos_wifi_disconnect(void);
diff --git a/code/esp32/components/eos/msgq.c b/code/esp32/components/eos/msgq.c
new file mode 100644
index 0000000..c704399
--- /dev/null
+++ b/code/esp32/components/eos/msgq.c
@@ -0,0 +1,69 @@
+#include <stdlib.h>
+
+#include "eos.h"
+#include "msgq.h"
+
+#define IDX_MASK(IDX, SIZE) ((IDX) & ((SIZE) - 1))
+
+void eos_msgq_init(EOSMsgQ *msgq, EOSMsgItem *array, uint8_t size) {
+ msgq->idx_r = 0;
+ msgq->idx_w = 0;
+ msgq->size = size;
+ msgq->array = array;
+}
+
+int eos_msgq_push(EOSMsgQ *msgq, unsigned char type, unsigned char *buffer, uint16_t len, uint8_t flags) {
+ if ((uint8_t)(msgq->idx_w - msgq->idx_r) == msgq->size) return EOS_ERR_FULL;
+
+ uint8_t idx = IDX_MASK(msgq->idx_w, msgq->size);
+ msgq->array[idx].type = type;
+ msgq->array[idx].buffer = buffer;
+ msgq->array[idx].len = len;
+ msgq->array[idx].flags = flags;
+ msgq->idx_w++;
+ return EOS_OK;
+}
+
+void eos_msgq_pop(EOSMsgQ *msgq, unsigned char *type, unsigned char **buffer, uint16_t *len, uint8_t *flags) {
+ if (msgq->idx_r == msgq->idx_w) {
+ *type = 0;
+ *buffer = NULL;
+ *len = 0;
+ if (flags) *flags = 0;
+ } else {
+ uint8_t idx = IDX_MASK(msgq->idx_r, msgq->size);
+ *type = msgq->array[idx].type;
+ *buffer = msgq->array[idx].buffer;
+ *len = msgq->array[idx].len;
+ if (flags) *flags = msgq->array[idx].flags;
+ msgq->idx_r++;
+ }
+}
+
+uint8_t eos_msgq_len(EOSMsgQ *msgq) {
+ return (uint8_t)(msgq->idx_w - msgq->idx_r);
+}
+
+void eos_bufq_init(EOSBufQ *bufq, unsigned char **array, uint8_t size) {
+ bufq->idx_r = 0;
+ bufq->idx_w = 0;
+ bufq->size = size;
+ bufq->array = array;
+}
+
+int eos_bufq_push(EOSBufQ *bufq, unsigned char *buffer) {
+ if ((uint8_t)(bufq->idx_w - bufq->idx_r) == bufq->size) return EOS_ERR_FULL;
+
+ bufq->array[IDX_MASK(bufq->idx_w++, bufq->size)] = buffer;
+ return EOS_OK;
+}
+
+unsigned char *eos_bufq_pop(EOSBufQ *bufq) {
+ if (bufq->idx_r == bufq->idx_w) return NULL;
+
+ return bufq->array[IDX_MASK(bufq->idx_r++, bufq->size)];
+}
+
+uint8_t eos_bufq_len(EOSBufQ *bufq) {
+ return (uint8_t)(bufq->idx_w - bufq->idx_r);
+}
diff --git a/code/esp32/components/eos/net.c b/code/esp32/components/eos/net.c
new file mode 100644
index 0000000..9a4a024
--- /dev/null
+++ b/code/esp32/components/eos/net.c
@@ -0,0 +1,285 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+#include <freertos/task.h>
+
+#include <esp_system.h>
+#include <esp_log.h>
+#include <esp_err.h>
+#include <esp_heap_caps.h>
+#include <driver/gpio.h>
+#include <driver/spi_slave.h>
+
+#include "eos.h"
+#include "msgq.h"
+#include "power.h"
+#include "net.h"
+
+#define SPI_GPIO_RTS 22
+#define SPI_GPIO_CTS 21
+#define SPI_GPIO_MOSI 23
+#define SPI_GPIO_MISO 19
+#define SPI_GPIO_SCLK 18
+#define SPI_GPIO_CS 5
+
+#define SPI_SIZE_BUF (EOS_NET_SIZE_BUF + 8)
+
+static volatile char net_sleep = 0;
+
+static EOSBufQ net_buf_q;
+static unsigned char *net_bufq_array[EOS_NET_SIZE_BUFQ];
+
+static EOSMsgQ net_send_q;
+static EOSMsgItem net_sndq_array[EOS_NET_SIZE_SNDQ];
+
+static SemaphoreHandle_t mutex;
+static SemaphoreHandle_t semaph;
+static TaskHandle_t net_xchg_task_handle;
+static const char *TAG = "EOS NET";
+
+static eos_net_fptr_t mtype_handler[EOS_NET_MAX_MTYPE];
+
+static void bad_handler(unsigned char mtype, unsigned char *buffer, uint16_t len) {
+ ESP_LOGE(TAG, "bad handler: %d len: %d", mtype, len);
+}
+
+// Called after a transaction is queued and ready for pickup by master. We use this to set the handshake line high.
+static void _post_setup_cb(spi_slave_transaction_t *trans) {
+ gpio_set_level(SPI_GPIO_CTS, 1);
+}
+
+// Called after transaction is sent/received. We use this to set the handshake line low.
+static void _post_trans_cb(spi_slave_transaction_t *trans) {
+ gpio_set_level(SPI_GPIO_CTS, 0);
+}
+
+static void net_xchg_task(void *pvParameters) {
+ int repeat = 0;
+ int wake = 0;
+ unsigned char mtype = 0;
+ unsigned char *buffer;
+ uint16_t len;
+ uint8_t flags;
+ unsigned char *buf_send = heap_caps_malloc(SPI_SIZE_BUF, MALLOC_CAP_DMA);
+ unsigned char *buf_recv = heap_caps_malloc(SPI_SIZE_BUF, MALLOC_CAP_DMA);
+ esp_err_t ret;
+ spi_slave_transaction_t spi_tr;
+
+ //Configuration for the SPI bus
+ spi_bus_config_t spi_bus_cfg = {
+ .mosi_io_num = SPI_GPIO_MOSI,
+ .miso_io_num = SPI_GPIO_MISO,
+ .sclk_io_num = SPI_GPIO_SCLK
+ };
+
+ //Configuration for the SPI slave interface
+ spi_slave_interface_config_t spi_slave_cfg = {
+ .mode = 0,
+ .spics_io_num = SPI_GPIO_CS,
+ .queue_size = 2,
+ .flags = 0,
+ .post_setup_cb = _post_setup_cb,
+ .post_trans_cb = _post_trans_cb
+ };
+
+ //Initialize SPI slave interface
+ ret = spi_slave_initialize(VSPI_HOST, &spi_bus_cfg, &spi_slave_cfg, 1);
+ assert(ret == ESP_OK);
+
+ memset(&spi_tr, 0, sizeof(spi_tr));
+ spi_tr.length = SPI_SIZE_BUF * 8;
+ spi_tr.tx_buffer = buf_send;
+ spi_tr.rx_buffer = buf_recv;
+
+ if (eos_power_wakeup_cause()) {
+ wake = 1;
+ repeat = 1;
+ }
+
+ eos_power_wait4init();
+ while (1) {
+ if (!repeat) {
+ xSemaphoreTake(mutex, portMAX_DELAY);
+
+ eos_msgq_pop(&net_send_q, &mtype, &buffer, &len, &flags);
+ if (mtype) {
+ buf_send[0] = mtype;
+ buf_send[1] = len >> 8;
+ buf_send[2] = len & 0xFF;
+ if (buffer) {
+ memcpy(buf_send + 3, buffer, len);
+ if (flags & EOS_NET_FLAG_BFREE) {
+ free(buffer);
+ } else {
+ eos_bufq_push(&net_buf_q, buffer);
+ xSemaphoreGive(semaph);
+ }
+ }
+ } else {
+ gpio_set_level(SPI_GPIO_RTS, 0);
+ buf_send[0] = 0;
+ buf_send[1] = 0;
+ buf_send[2] = 0;
+ }
+
+ xSemaphoreGive(mutex);
+ }
+ repeat = 0;
+
+ buf_recv[0] = 0;
+ buf_recv[1] = 0;
+ buf_recv[2] = 0;
+ spi_slave_transmit(VSPI_HOST, &spi_tr, portMAX_DELAY);
+ ESP_LOGD(TAG, "RECV:%d", buf_recv[0]);
+
+ if (wake) {
+ eos_power_net_ready();
+ wake = 0;
+ }
+ if (buf_recv[0] == 0x00) continue;
+ if (buf_recv[0] == 0xFF) { // Sleep req
+ if (buf_send[0] == 0) {
+ int abort = 0;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ net_sleep = 1;
+ if (eos_msgq_len(&net_send_q)) abort = 1;
+ xSemaphoreGive(mutex);
+
+ spi_slave_free(VSPI_HOST);
+
+ eos_power_sleep();
+ if (abort) eos_power_wake(EOS_PWR_WAKE_MSG);
+
+ vTaskSuspend(NULL);
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ net_sleep = 0;
+ xSemaphoreGive(mutex);
+
+ spi_slave_initialize(VSPI_HOST, &spi_bus_cfg, &spi_slave_cfg, 1);
+ wake = 1;
+ repeat = 1;
+ }
+ continue;
+ }
+ mtype = buf_recv[0];
+ len = (uint16_t)buf_recv[1] << 8;
+ len |= (uint16_t)buf_recv[2] & 0xFF;
+ buffer = buf_recv + 3;
+ if (mtype & EOS_NET_MTYPE_FLAG_ONEW) {
+ mtype &= ~EOS_NET_MTYPE_FLAG_ONEW;
+ if (buf_send[0]) repeat = 1;
+ }
+ if (mtype <= EOS_NET_MAX_MTYPE) {
+ mtype_handler[mtype-1](mtype, buffer, len);
+ } else {
+ bad_handler(mtype, buffer, len);
+ }
+ }
+ vTaskDelete(NULL);
+}
+
+void eos_net_init(void) {
+ int i;
+
+ // Configuration for the handshake lines
+ gpio_config_t io_conf;
+
+ io_conf.intr_type = GPIO_INTR_DISABLE;
+ io_conf.mode = GPIO_MODE_OUTPUT;
+ io_conf.pull_up_en = 0;
+ io_conf.pull_down_en = 0;
+ io_conf.pin_bit_mask = ((uint64_t)1 << SPI_GPIO_CTS);
+ gpio_config(&io_conf);
+ gpio_set_level(SPI_GPIO_CTS, 0);
+
+ io_conf.intr_type = GPIO_INTR_DISABLE;
+ io_conf.mode = GPIO_MODE_OUTPUT;
+ io_conf.pull_up_en = 0;
+ io_conf.pull_down_en = 0;
+ io_conf.pin_bit_mask = ((uint64_t)1 << SPI_GPIO_RTS);
+ gpio_config(&io_conf);
+ gpio_set_level(SPI_GPIO_RTS, 0);
+
+ eos_msgq_init(&net_send_q, net_sndq_array, EOS_NET_SIZE_SNDQ);
+ eos_bufq_init(&net_buf_q, net_bufq_array, EOS_NET_SIZE_BUFQ);
+ for (i=0; i<EOS_NET_SIZE_BUFQ; i++) {
+ eos_bufq_push(&net_buf_q, malloc(EOS_NET_SIZE_BUF));
+ }
+
+ for (i=0; i<EOS_NET_MAX_MTYPE; i++) {
+ mtype_handler[i] = bad_handler;
+ }
+
+ semaph = xSemaphoreCreateCounting(EOS_NET_SIZE_BUFQ, EOS_NET_SIZE_BUFQ);
+ mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(mutex);
+ xTaskCreate(&net_xchg_task, "net_xchg", EOS_TASK_SSIZE_NET_XCHG, NULL, EOS_TASK_PRIORITY_NET_XCHG, &net_xchg_task_handle);
+ ESP_LOGI(TAG, "INIT");
+}
+
+unsigned char *eos_net_alloc(void) {
+ unsigned char *ret;
+
+ xSemaphoreTake(semaph, portMAX_DELAY);
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ ret = eos_bufq_pop(&net_buf_q);
+ xSemaphoreGive(mutex);
+
+ return ret;
+}
+
+void eos_net_free(unsigned char *buf) {
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ eos_bufq_push(&net_buf_q, buf);
+ xSemaphoreGive(semaph);
+ xSemaphoreGive(mutex);
+}
+
+int eos_net_send(unsigned char mtype, unsigned char *buffer, uint16_t len, uint8_t flags) {
+ int rv, sleep;
+
+ if (flags & EOS_NET_FLAG_BCOPY) xSemaphoreTake(semaph, portMAX_DELAY);
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ sleep = net_sleep;
+ gpio_set_level(SPI_GPIO_RTS, 1);
+ if (flags & EOS_NET_FLAG_BCOPY) {
+ unsigned char *b = eos_bufq_pop(&net_buf_q);
+ memcpy(b, buffer, len);
+ buffer = b;
+ }
+ rv = eos_msgq_push(&net_send_q, mtype, buffer, len, flags);
+ xSemaphoreGive(mutex);
+
+ if (sleep) eos_power_wake(EOS_PWR_WAKE_MSG);
+
+ return rv;
+}
+
+void eos_net_set_handler(unsigned char mtype, eos_net_fptr_t handler) {
+ mtype_handler[mtype-1] = handler;
+}
+
+void eos_net_sleep_done(uint8_t mode) {
+ gpio_set_level(SPI_GPIO_CTS, 1);
+ vTaskDelay(200 / portTICK_PERIOD_MS);
+ gpio_set_level(SPI_GPIO_CTS, 0);
+}
+
+void eos_net_wake(uint8_t source, uint8_t mode) {
+ int sleep;
+
+ if (mode == EOS_PWR_SMODE_DEEP) return;
+ do {
+ vTaskResume(net_xchg_task_handle);
+ vTaskDelay(10 / portTICK_PERIOD_MS);
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ sleep = net_sleep;
+ xSemaphoreGive(mutex);
+ } while (sleep);
+}
diff --git a/code/esp32/components/eos/power.c b/code/esp32/components/eos/power.c
new file mode 100644
index 0000000..f07e67b
--- /dev/null
+++ b/code/esp32/components/eos/power.c
@@ -0,0 +1,325 @@
+#include <stdlib.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/task.h>
+#include <freertos/queue.h>
+#include <driver/gpio.h>
+#include <esp_sleep.h>
+#include <esp_pm.h>
+#include <esp_log.h>
+
+#include "eos.h"
+#include "net.h"
+#include "cell.h"
+#include "power.h"
+
+#define POWER_GPIO_BTN 0
+#define POWER_GPIO_NET 5
+#define POWER_GPIO_UART 35
+
+#define POWER_ETYPE_BTN 1
+#define POWER_ETYPE_SLEEP 2
+#define POWER_ETYPE_WAKE 3
+#define POWER_ETYPE_NETRDY 4
+
+typedef struct {
+ uint8_t type;
+ union {
+ uint8_t source;
+ uint8_t level;
+ };
+} power_event_t;
+
+static esp_pm_lock_handle_t power_lock_cpu_freq;
+static esp_pm_lock_handle_t power_lock_apb_freq;
+static esp_pm_lock_handle_t power_lock_no_sleep;
+
+static const char *TAG = "EOS POWER";
+
+static QueueHandle_t power_queue;
+
+static volatile int init_done = 0;
+
+static void IRAM_ATTR btn_handler(void *arg) {
+ power_event_t evt;
+
+ evt.type = POWER_ETYPE_BTN;
+ evt.level = gpio_get_level(POWER_GPIO_BTN);
+ xQueueSendFromISR(power_queue, &evt, NULL);
+}
+
+static void IRAM_ATTR btn_wake_handler(void *arg) {
+ power_event_t evt;
+
+ gpio_intr_disable(POWER_GPIO_BTN);
+
+ evt.type = POWER_ETYPE_WAKE;
+ evt.source = EOS_PWR_WAKE_BTN;
+ xQueueSendFromISR(power_queue, &evt, NULL);
+
+}
+
+static void IRAM_ATTR net_wake_handler(void *arg) {
+ power_event_t evt;
+
+ gpio_intr_disable(POWER_GPIO_NET);
+
+ evt.type = POWER_ETYPE_WAKE;
+ evt.source = EOS_PWR_WAKE_NET;
+ xQueueSendFromISR(power_queue, &evt, NULL);
+}
+
+static void IRAM_ATTR uart_wake_handler(void *arg) {
+ power_event_t evt;
+
+ gpio_intr_disable(POWER_GPIO_UART);
+
+ evt.type = POWER_ETYPE_WAKE;
+ evt.source = EOS_PWR_WAKE_UART;
+ xQueueSendFromISR(power_queue, &evt, NULL);
+}
+
+void power_sleep(uint8_t mode) {
+ gpio_config_t io_conf;
+
+ eos_modem_sleep(mode);
+ eos_net_sleep_done(mode);
+
+ switch (mode) {
+ case EOS_PWR_SMODE_LIGHT:
+ io_conf.intr_type = GPIO_INTR_DISABLE;
+ io_conf.mode = GPIO_MODE_INPUT;
+ io_conf.pin_bit_mask = ((uint64_t)1 << POWER_GPIO_NET);
+ io_conf.pull_up_en = 0;
+ io_conf.pull_down_en = 0;
+ gpio_config(&io_conf);
+
+ gpio_isr_handler_add(POWER_GPIO_BTN, btn_wake_handler, NULL);
+ gpio_isr_handler_add(POWER_GPIO_NET, net_wake_handler, NULL);
+ gpio_isr_handler_add(POWER_GPIO_UART, uart_wake_handler, NULL);
+
+ esp_sleep_enable_gpio_wakeup();
+ gpio_wakeup_enable(POWER_GPIO_BTN, GPIO_INTR_LOW_LEVEL);
+ gpio_wakeup_enable(POWER_GPIO_NET, GPIO_INTR_LOW_LEVEL);
+ gpio_wakeup_enable(POWER_GPIO_UART, GPIO_INTR_LOW_LEVEL);
+
+ ESP_LOGD(TAG, "SLEEP");
+
+ esp_pm_lock_release(power_lock_apb_freq);
+ esp_pm_lock_release(power_lock_no_sleep);
+
+ break;
+
+ case EOS_PWR_SMODE_DEEP:
+ gpio_deep_sleep_hold_en();
+ esp_sleep_enable_ext0_wakeup(POWER_GPIO_BTN, 0);
+ esp_sleep_enable_ext1_wakeup((uint64_t)1 << POWER_GPIO_UART, ESP_EXT1_WAKEUP_ALL_LOW);
+
+ ESP_LOGD(TAG, "SLEEP");
+
+ esp_deep_sleep_start();
+ break;
+
+ default:
+ break;
+ }
+}
+
+void power_wake_stage1(uint8_t source, uint8_t mode) {
+ gpio_config_t io_conf;
+
+ if (mode == EOS_PWR_SMODE_LIGHT) {
+ esp_pm_lock_acquire(power_lock_apb_freq);
+ esp_pm_lock_acquire(power_lock_no_sleep);
+
+ gpio_wakeup_disable(POWER_GPIO_BTN);
+ gpio_wakeup_disable(POWER_GPIO_NET);
+ gpio_wakeup_disable(POWER_GPIO_UART);
+
+ gpio_isr_handler_remove(POWER_GPIO_NET);
+ io_conf.intr_type = GPIO_INTR_DISABLE;
+ io_conf.mode = GPIO_MODE_DISABLE;
+ io_conf.pin_bit_mask = ((uint64_t)1 << POWER_GPIO_NET);
+ io_conf.pull_up_en = 0;
+ io_conf.pull_down_en = 0;
+ gpio_config(&io_conf);
+ }
+
+ gpio_intr_disable(POWER_GPIO_BTN);
+ if ((source != EOS_PWR_WAKE_BTN) && (source != EOS_PWR_WAKE_NET)) {
+ gpio_set_direction(POWER_GPIO_BTN, GPIO_MODE_OUTPUT);
+ gpio_set_level(POWER_GPIO_BTN, 0);
+ vTaskDelay(200 / portTICK_PERIOD_MS);
+ gpio_set_direction(POWER_GPIO_BTN, GPIO_MODE_INPUT);
+ }
+
+ eos_net_wake(source, mode);
+}
+
+void power_wake_stage2(uint8_t source, uint8_t mode) {
+ eos_modem_wake(source, mode);
+
+ gpio_set_intr_type(POWER_GPIO_BTN, GPIO_INTR_ANYEDGE);
+ gpio_isr_handler_add(POWER_GPIO_BTN, btn_handler, NULL);
+
+ ESP_LOGD(TAG, "WAKE");
+}
+
+static void power_event_task(void *pvParameters) {
+ unsigned char *buf;
+ power_event_t evt;
+ uint8_t source;
+ uint8_t wakeup_cause;
+ uint8_t mode;
+ int sleep;
+
+ source = 0;
+ wakeup_cause = eos_power_wakeup_cause();
+ if (wakeup_cause) {
+ mode = EOS_PWR_SMODE_DEEP;
+ sleep = 1;
+ } else {
+ mode = EOS_PWR_SMODE_LIGHT;
+ sleep = 0;
+ }
+
+ while (1) {
+ if (xQueueReceive(power_queue, &evt, portMAX_DELAY)) {
+ switch (evt.type) {
+ case POWER_ETYPE_SLEEP:
+ if (!sleep) {
+ mode = EOS_PWR_SMODE_DEEP;
+ power_sleep(mode);
+ sleep = 1;
+ }
+ break;
+
+ case POWER_ETYPE_WAKE:
+ if (sleep) {
+ source = evt.source;
+ power_wake_stage1(source, mode);
+ }
+ break;
+
+ case POWER_ETYPE_NETRDY:
+ if (sleep && source) {
+ power_wake_stage2(source, mode);
+ sleep = 0;
+ source = 0;
+ }
+ break;
+
+ case POWER_ETYPE_BTN:
+ buf = eos_net_alloc();
+ buf[0] = EOS_PWR_MTYPE_BUTTON;
+ buf[1] = evt.level;
+ eos_net_send(EOS_NET_MTYPE_POWER, buf, 2, 0);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ vTaskDelete(NULL);
+}
+
+void eos_power_init(void) {
+ esp_err_t ret;
+ gpio_config_t io_conf;
+ esp_pm_config_esp32_t pwr_conf;
+ uint8_t wakeup_cause;
+
+ io_conf.intr_type = GPIO_INTR_ANYEDGE;
+ io_conf.mode = GPIO_MODE_INPUT;
+ io_conf.pin_bit_mask = ((uint64_t)1 << POWER_GPIO_BTN);
+ io_conf.pull_up_en = 1;
+ io_conf.pull_down_en = 0;
+ gpio_config(&io_conf);
+ gpio_isr_handler_add(POWER_GPIO_BTN, btn_handler, NULL);
+
+ /*
+ ret = esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
+ assert(ret == ESP_OK);
+ ret = esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_ON);
+ assert(ret == ESP_OK);
+ ret = esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON);
+ assert(ret == ESP_OK);
+ */
+
+ ret = esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, NULL, &power_lock_cpu_freq);
+ assert(ret == ESP_OK);
+ ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, NULL, &power_lock_apb_freq);
+ assert(ret == ESP_OK);
+ ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, NULL, &power_lock_no_sleep);
+ assert(ret == ESP_OK);
+
+ ret = esp_pm_lock_acquire(power_lock_cpu_freq);
+ assert(ret == ESP_OK);
+ ret = esp_pm_lock_acquire(power_lock_apb_freq);
+ assert(ret == ESP_OK);
+ ret = esp_pm_lock_acquire(power_lock_no_sleep);
+ assert(ret == ESP_OK);
+
+ pwr_conf.max_freq_mhz = 160;
+ pwr_conf.min_freq_mhz = 80;
+ pwr_conf.light_sleep_enable = 1;
+
+ ret = esp_pm_configure(&pwr_conf);
+ assert(ret == ESP_OK);
+
+ power_queue = xQueueCreate(4, sizeof(power_event_t));
+ xTaskCreate(power_event_task, "power_event", EOS_TASK_SSIZE_PWR, NULL, EOS_TASK_PRIORITY_PWR, NULL);
+
+ wakeup_cause = eos_power_wakeup_cause();
+ if (wakeup_cause) eos_power_wake(wakeup_cause);
+
+ init_done = 1;
+ ESP_LOGI(TAG, "INIT");
+}
+
+void eos_power_wait4init(void) {
+ while (!init_done);
+}
+
+uint8_t eos_power_wakeup_cause(void) {
+ esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
+
+ switch (cause) {
+ case ESP_SLEEP_WAKEUP_EXT0:
+ return EOS_PWR_WAKE_BTN;
+
+ case ESP_SLEEP_WAKEUP_EXT1:
+ return EOS_PWR_WAKE_UART;
+
+ default:
+ case ESP_SLEEP_WAKEUP_UNDEFINED:
+ return EOS_PWR_WAKE_RST;
+ }
+}
+
+void eos_power_sleep(void) {
+ power_event_t evt;
+
+ evt.type = POWER_ETYPE_SLEEP;
+ evt.source = 0;
+ xQueueSend(power_queue, &evt, portMAX_DELAY);
+}
+
+void eos_power_wake(uint8_t source) {
+ power_event_t evt;
+
+ evt.type = POWER_ETYPE_WAKE;
+ evt.source = source;
+
+ xQueueSend(power_queue, &evt, portMAX_DELAY);
+}
+
+void eos_power_net_ready(void) {
+ power_event_t evt;
+
+ evt.type = POWER_ETYPE_NETRDY;
+ evt.source = 0;
+
+ xQueueSend(power_queue, &evt, portMAX_DELAY);
+}
diff --git a/code/esp32/components/eos/sock.c b/code/esp32/components/eos/sock.c
new file mode 100644
index 0000000..17357e4
--- /dev/null
+++ b/code/esp32/components/eos/sock.c
@@ -0,0 +1,163 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/semphr.h>
+#include <freertos/task.h>
+
+#include <esp_system.h>
+#include <esp_event.h>
+#include <esp_event_loop.h>
+#include <esp_log.h>
+#include <esp_err.h>
+#include <esp_wifi.h>
+#include <nvs_flash.h>
+
+#include <lwip/sockets.h>
+#include <lwip/err.h>
+#include <lwip/sockets.h>
+#include <lwip/sys.h>
+#include <lwip/netdb.h>
+#include <lwip/dns.h>
+
+#include "eos.h"
+#include "net.h"
+#include "sock.h"
+
+static const char *TAG = "EOS SOCK";
+static SemaphoreHandle_t mutex;
+static int _socks[EOS_SOCK_MAX_SOCK];
+
+static int t_open_dgram(void) {
+ struct sockaddr_in _myaddr;
+ int sock;
+
+ sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) return sock;
+
+ memset((char *)&_myaddr, 0, sizeof(_myaddr));
+ _myaddr.sin_family = AF_INET;
+ _myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ _myaddr.sin_port = htons(3000);
+
+ int rv = bind(sock, (struct sockaddr *)&_myaddr, sizeof(_myaddr));
+ if (rv < 0) {
+ close(sock);
+ return rv;
+ }
+ return sock;
+}
+
+static void t_close(int sock) {
+ close(sock);
+}
+
+static ssize_t t_sendto(int sock, void *msg, size_t msg_size, EOSNetAddr *addr) {
+ struct sockaddr_in servaddr;
+
+ memset((void *)&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_port = addr->port;
+ memcpy((void *)&servaddr.sin_addr, addr->host, sizeof(addr->host));
+ return sendto(sock, msg, msg_size, 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
+}
+
+static ssize_t t_recvfrom(int sock, void *msg, size_t msg_size, EOSNetAddr *addr) {
+ struct sockaddr_in servaddr;
+ socklen_t addrlen = sizeof(servaddr);
+ memset((void *)&servaddr, 0, sizeof(servaddr));
+
+ ssize_t recvlen = recvfrom(sock, msg, msg_size, 0, (struct sockaddr *)&servaddr, &addrlen);
+ if (recvlen < 0) return recvlen;
+
+ if (addr) {
+ addr->port = servaddr.sin_port;
+ memcpy(addr->host, (void *)&servaddr.sin_addr, sizeof(addr->host));
+ }
+ return recvlen;
+}
+
+static void udp_rcvr_task(void *pvParameters) {
+ EOSNetAddr addr;
+ uint8_t esock = (uint8_t)pvParameters;
+ int sock = _socks[esock-1];
+ unsigned char *buf;
+
+ do {
+ ssize_t rv;
+
+ buf = eos_net_alloc();
+ rv = t_recvfrom(sock, buf+EOS_SOCK_SIZE_UDP_HDR, EOS_NET_SIZE_BUF-EOS_SOCK_SIZE_UDP_HDR, &addr);
+ if (rv < 0) {
+ sock = 0;
+ ESP_LOGE(TAG, "UDP RECV ERR:%d", rv);
+ continue;
+ }
+ buf[0] = EOS_SOCK_MTYPE_PKT;
+ buf[1] = esock;
+ memcpy(buf+2, addr.host, sizeof(addr.host));
+ memcpy(buf+2+sizeof(addr.host), &addr.port, sizeof(addr.port));
+ eos_net_send(EOS_NET_MTYPE_SOCK, buf, rv+EOS_SOCK_SIZE_UDP_HDR, 0);
+ } while(sock);
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ _socks[esock-1] = 0;
+ xSemaphoreGive(mutex);
+ vTaskDelete(NULL);
+}
+
+static void sock_handler(unsigned char type, unsigned char *buffer, uint16_t size) {
+ EOSNetAddr addr;
+ uint8_t esock;
+ int sock, i;
+ unsigned char *rbuf;
+
+ if (size < 1) return;
+
+ switch(buffer[0]) {
+ case EOS_SOCK_MTYPE_PKT:
+ if (size < EOS_SOCK_SIZE_UDP_HDR) return;
+ sock = _socks[buffer[1]-1];
+ memcpy(addr.host, buffer+2, sizeof(addr.host));
+ memcpy(&addr.port, buffer+2+sizeof(addr.host), sizeof(addr.port));
+ t_sendto(sock, buffer+EOS_SOCK_SIZE_UDP_HDR, size-EOS_SOCK_SIZE_UDP_HDR, &addr);
+ break;
+
+ case EOS_SOCK_MTYPE_OPEN_DGRAM:
+ sock = t_open_dgram();
+ esock = 0;
+ if (sock > 0) {
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ for (i=0; i<EOS_SOCK_MAX_SOCK; i++) {
+ if (_socks[i] == 0) {
+ esock = i+1;
+ _socks[i] = sock;
+ }
+ }
+ xSemaphoreGive(mutex);
+ }
+ // xTaskCreatePinnedToCore(&sock_receiver, "sock_receiver", EOS_TASK_SSIZE_UDP_RCVR, (void *)esock, EOS_TASK_PRIORITY_UDP_RCVR, NULL, 1);
+ xTaskCreate(&udp_rcvr_task, "udp_rcvr", EOS_TASK_SSIZE_UDP_RCVR, (void *)esock, EOS_TASK_PRIORITY_UDP_RCVR, NULL);
+ rbuf = eos_net_alloc();
+ rbuf[0] = EOS_SOCK_MTYPE_OPEN_DGRAM;
+ rbuf[1] = esock;
+ eos_net_send(EOS_NET_MTYPE_SOCK, rbuf, 2, 0);
+ break;
+
+ case EOS_SOCK_MTYPE_CLOSE:
+ if (size < 2) return;
+ sock = _socks[buffer[1]-1];
+ t_close(sock);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void eos_sock_init(void) {
+ mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(mutex);
+ eos_net_set_handler(EOS_NET_MTYPE_SOCK, sock_handler);
+ ESP_LOGI(TAG, "INIT");
+} \ No newline at end of file
diff --git a/code/esp32/components/eos/wifi.c b/code/esp32/components/eos/wifi.c
new file mode 100755
index 0000000..3dd90ba
--- /dev/null
+++ b/code/esp32/components/eos/wifi.c
@@ -0,0 +1,302 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/task.h>
+
+#include <esp_system.h>
+#include <esp_event.h>
+#include <esp_event_loop.h>
+#include <esp_log.h>
+#include <esp_err.h>
+#include <esp_wifi.h>
+
+#include "eos.h"
+#include "net.h"
+#include "wifi.h"
+
+// XXX: WiFi fail due to no DHCP server
+
+#define WIFI_MAX_SCAN_RECORDS 20
+#define WIFI_MAX_CONNECT_ATTEMPTS 3
+
+#define WIFI_STATE_STOPPED 0
+#define WIFI_STATE_SCANNING 1
+#define WIFI_STATE_CONNECTING 2
+#define WIFI_STATE_CONNECTED 3
+#define WIFI_STATE_DISCONNECTING 4
+#define WIFI_STATE_DISCONNECTED 5
+
+#define WIFI_ACTION_NONE 0
+#define WIFI_ACTION_SCAN 1
+#define WIFI_ACTION_CONNECT 2
+#define WIFI_ACTION_DISCONNECT 3
+
+static const char *TAG = "EOS WIFI";
+
+static SemaphoreHandle_t mutex;
+
+static wifi_config_t wifi_sta_config;
+static wifi_scan_config_t wifi_scan_config;
+
+static int wifi_connect_cnt = 0;
+static uint8_t wifi_action;
+static uint8_t wifi_state;
+
+static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) {
+ esp_err_t ret = ESP_OK;
+ char _disconnect;
+ uint8_t _action, _state;
+ unsigned char *rbuf;
+ wifi_ap_record_t scan_r[WIFI_MAX_SCAN_RECORDS];
+ uint16_t scan_n = WIFI_MAX_SCAN_RECORDS;
+
+ switch(event->event_id) {
+ case WIFI_EVENT_SCAN_DONE:
+ memset(scan_r, 0, sizeof(scan_r));
+ esp_wifi_scan_get_ap_records(&scan_n, scan_r);
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ _state = wifi_state;
+ if (wifi_state == WIFI_STATE_CONNECTED) wifi_action = WIFI_ACTION_NONE;
+ xSemaphoreGive(mutex);
+
+ if (_state != WIFI_STATE_CONNECTED) ret = esp_wifi_stop();
+ break;
+
+ case WIFI_EVENT_STA_START:
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ _action = wifi_action;
+ xSemaphoreGive(mutex);
+
+ switch (_action) {
+ case WIFI_ACTION_SCAN:
+ ret = esp_wifi_scan_start(&wifi_scan_config, 0);
+ break;
+
+ case WIFI_ACTION_CONNECT:
+ ret = esp_wifi_connect();
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case WIFI_EVENT_STA_STOP:
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ wifi_state = WIFI_STATE_STOPPED;
+ wifi_action = WIFI_ACTION_NONE;
+ xSemaphoreGive(mutex);
+ break;
+
+ case WIFI_EVENT_STA_CONNECTED:
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ wifi_state = WIFI_STATE_CONNECTED;
+ wifi_action = WIFI_ACTION_NONE;
+ wifi_connect_cnt = WIFI_MAX_CONNECT_ATTEMPTS;
+ xSemaphoreGive(mutex);
+ break;
+
+ case WIFI_EVENT_STA_DISCONNECTED:
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (wifi_connect_cnt) wifi_connect_cnt--;
+ _action = wifi_action;
+ _disconnect = (wifi_connect_cnt == 0);
+ if (_disconnect) wifi_state = WIFI_STATE_DISCONNECTED;
+ xSemaphoreGive(mutex);
+
+ if (_disconnect) {
+ rbuf = eos_net_alloc();
+ if (_action == WIFI_ACTION_CONNECT) {
+ rbuf[0] = EOS_WIFI_MTYPE_CONNECT;
+ rbuf[1] = EOS_ERR;
+ eos_net_send(EOS_NET_MTYPE_WIFI, rbuf, 2, 0);
+ } else {
+ rbuf[0] = EOS_WIFI_MTYPE_DISCONNECT;
+ eos_net_send(EOS_NET_MTYPE_WIFI, rbuf, 1, 0);
+ }
+ if (!_action) ret = esp_wifi_stop();
+ } else {
+ ret = esp_wifi_connect();
+ }
+ break;
+
+ case IP_EVENT_STA_GOT_IP:
+ ESP_LOGD(TAG, "IP address: " IPSTR, IP2STR(&event->event_info.got_ip.ip_info.ip));
+ if (event->event_info.got_ip.ip_changed) {
+ // send wifi reconnect
+ } else {
+ rbuf = eos_net_alloc();
+ rbuf[0] = EOS_WIFI_MTYPE_CONNECT;
+ rbuf[1] = EOS_OK;
+ eos_net_send(EOS_NET_MTYPE_WIFI, rbuf, 2, 0);
+ }
+ break;
+
+ default: // Ignore the other event types
+ break;
+ }
+ if (ret != ESP_OK) ESP_LOGD(TAG, "ESP WIFI ERR: %d", ret);
+
+ return ESP_OK;
+}
+
+static void wifi_handler(unsigned char _mtype, unsigned char *buffer, uint16_t size) {
+ int rv = EOS_OK;
+ uint8_t mtype = buffer[0];
+ char *ssid, *pass;
+
+ switch (mtype) {
+ case EOS_WIFI_MTYPE_SCAN:
+ rv = eos_wifi_scan();
+ break;
+
+ case EOS_WIFI_MTYPE_CONNECT:
+ ssid = (char *)buffer+1;
+ pass = ssid+strlen(ssid)+1;
+ rv = eos_wifi_set_auth(ssid, pass);
+ if (!rv) rv = eos_wifi_connect();
+ break;
+
+ case EOS_WIFI_MTYPE_DISCONNECT:
+ rv = eos_wifi_disconnect();
+ break;
+ }
+ if (rv) ESP_LOGD(TAG, "WIFI HANDLER ERR: %d", rv);
+}
+
+void eos_wifi_init(void) {
+ esp_err_t ret;
+ wifi_init_config_t wifi_config = WIFI_INIT_CONFIG_DEFAULT();
+
+ memset(&wifi_sta_config, 0, sizeof(wifi_sta_config));
+
+ ret = esp_event_loop_init(wifi_event_handler, NULL);
+ assert(ret == ESP_OK);
+
+ ret = esp_wifi_init(&wifi_config);
+ assert(ret == ESP_OK);
+
+ ret = esp_wifi_set_storage(WIFI_STORAGE_RAM);
+ assert(ret == ESP_OK);
+
+ ret = esp_wifi_set_mode(WIFI_MODE_STA);
+ assert(ret == ESP_OK);
+
+ ret = esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_sta_config);
+ assert(ret == ESP_OK);
+
+ ret = esp_wifi_stop();
+ assert(ret == ESP_OK);
+
+ mutex = xSemaphoreCreateBinary();
+ xSemaphoreGive(mutex);
+
+ eos_net_set_handler(EOS_NET_MTYPE_WIFI, wifi_handler);
+ ESP_LOGI(TAG, "INIT");
+}
+
+int eos_wifi_scan(void) {
+ int rv = EOS_OK;
+ esp_err_t ret = ESP_OK;
+ uint8_t _wifi_state = 0;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (!wifi_action) {
+ _wifi_state = wifi_state;
+
+ wifi_action = WIFI_ACTION_SCAN;
+ if (wifi_state == WIFI_STATE_STOPPED) wifi_state = WIFI_STATE_SCANNING;
+
+ memset(&wifi_scan_config, 0, sizeof(wifi_scan_config));
+ } else {
+ rv = EOS_ERR_BUSY;
+ }
+ xSemaphoreGive(mutex);
+
+ if (rv) return rv;
+
+ if (_wifi_state == WIFI_STATE_STOPPED) {
+ ret = esp_wifi_start();
+ } else {
+ ret = esp_wifi_scan_start(&wifi_scan_config, 0);
+ }
+ if (ret != ESP_OK) rv = EOS_ERR;
+
+ return rv;
+}
+
+int eos_wifi_set_auth(char *ssid, char *pass) {
+ int rv = EOS_OK;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (!wifi_action) {
+ if (ssid) strncpy((char *)wifi_sta_config.sta.ssid, ssid, sizeof(wifi_sta_config.sta.ssid) - 1);
+ if (pass) strncpy((char *)wifi_sta_config.sta.password, pass, sizeof(wifi_sta_config.sta.password) - 1);
+ } else {
+ rv = EOS_ERR_BUSY;
+
+ }
+ xSemaphoreGive(mutex);
+
+ return rv;
+}
+
+int eos_wifi_connect(void) {
+ int rv = EOS_OK;
+ esp_err_t ret = ESP_OK;
+ uint8_t _wifi_state = 0;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (!wifi_action) {
+ _wifi_state = wifi_state;
+
+ wifi_action = WIFI_ACTION_CONNECT;
+ wifi_state = WIFI_STATE_CONNECTING;
+ wifi_connect_cnt = WIFI_MAX_CONNECT_ATTEMPTS;
+ } else {
+ rv = EOS_ERR_BUSY;
+ }
+ xSemaphoreGive(mutex);
+
+ if (rv) return rv;
+
+ esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_sta_config);
+
+ if (_wifi_state == WIFI_STATE_STOPPED) {
+ ret = esp_wifi_start();
+ } else {
+ ret = esp_wifi_connect();
+ }
+ if (ret != ESP_OK) rv = EOS_ERR;
+
+ return rv;
+}
+
+int eos_wifi_disconnect(void) {
+ int rv = EOS_OK;
+ esp_err_t ret = ESP_OK;
+
+ xSemaphoreTake(mutex, portMAX_DELAY);
+ if (!wifi_action) {
+ wifi_action = WIFI_ACTION_DISCONNECT;
+ wifi_state = WIFI_STATE_DISCONNECTING;
+ wifi_connect_cnt = 0;
+
+ memset(wifi_sta_config.sta.ssid, 0, sizeof(wifi_sta_config.sta.ssid));
+ memset(wifi_sta_config.sta.password, 0, sizeof(wifi_sta_config.sta.password));
+ } else {
+ rv = EOS_ERR_BUSY;
+ }
+ xSemaphoreGive(mutex);
+
+ if (rv) return rv;
+
+ ret = esp_wifi_stop();
+ if (ret != ESP_OK) rv = EOS_ERR;
+
+ return rv;
+}
+
diff --git a/code/esp32/main/app_main.c b/code/esp32/main/app_main.c
new file mode 100644
index 0000000..b700a8e
--- /dev/null
+++ b/code/esp32/main/app_main.c
@@ -0,0 +1,38 @@
+#include <freertos/FreeRTOS.h>
+#include <freertos/task.h>
+
+#include <tcpip_adapter.h>
+#include <driver/gpio.h>
+#include <esp_sleep.h>
+
+#include "i2c.h"
+#include "cell.h"
+#include "_net.h"
+#include "wifi.h"
+#include "sock.h"
+#include "power.h"
+#include "bq25895.h"
+
+#define ESP_INTR_FLAG_DEFAULT 0
+
+// Main application
+void app_main() {
+ tcpip_adapter_init();
+
+ eos_net_init();
+
+ eos_pcm_init();
+ gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
+ eos_modem_init();
+
+ eos_cell_init();
+ eos_wifi_init();
+ eos_sock_init();
+
+ eos_power_init();
+
+ eos_i2c_init();
+ eos_bq25895_set_ilim();
+}
+
+
diff --git a/code/esp32/main/component.mk b/code/esp32/main/component.mk
new file mode 100644
index 0000000..61f8990
--- /dev/null
+++ b/code/esp32/main/component.mk
@@ -0,0 +1,8 @@
+#
+# Main component makefile.
+#
+# This Makefile can be left empty. By default, it will take the sources in the
+# src/ directory, compile them and link them into lib(subdirectory_name).a
+# in the build directory. This behaviour is entirely configurable,
+# please read the ESP-IDF documents if you need to do this.
+#