summaryrefslogtreecommitdiff
path: root/fw/esp32
diff options
context:
space:
mode:
authorUros Majstorovic <majstor@majstor.org>2020-08-05 03:39:22 +0200
committerUros Majstorovic <majstor@majstor.org>2020-08-05 03:39:22 +0200
commitcf7c06297d04bade9cd04c056f9ed510e64dd7bd (patch)
treea3b8cc23574b98e10874b51d33c9fe1bfc012663 /fw/esp32
parent5cd610a07468137066ea4daa5176c3e7045113b0 (diff)
code -> fw
Diffstat (limited to 'fw/esp32')
-rw-r--r--fw/esp32/LICENSE339
-rw-r--r--fw/esp32/Makefile9
-rw-r--r--fw/esp32/components/eos/at_cmd.c164
-rw-r--r--fw/esp32/components/eos/bq25895.c60
-rw-r--r--fw/esp32/components/eos/cell.c68
-rw-r--r--fw/esp32/components/eos/cell_data.c20
-rw-r--r--fw/esp32/components/eos/cell_modem.c767
-rw-r--r--fw/esp32/components/eos/cell_pcm.c258
-rw-r--r--fw/esp32/components/eos/cell_sms.c22
-rw-r--r--fw/esp32/components/eos/cell_ussd.c34
-rw-r--r--fw/esp32/components/eos/cell_voice.c20
-rw-r--r--fw/esp32/components/eos/component.mk0
-rw-r--r--fw/esp32/components/eos/drv2605l.c82
-rw-r--r--fw/esp32/components/eos/gsm.c453
-rw-r--r--fw/esp32/components/eos/gsm_cp.c109
-rw-r--r--fw/esp32/components/eos/i2c.c103
-rw-r--r--fw/esp32/components/eos/include/_net.h1
-rw-r--r--fw/esp32/components/eos/include/at_cmd.h20
-rw-r--r--fw/esp32/components/eos/include/bq25895.h3
-rw-r--r--fw/esp32/components/eos/include/cell.h68
-rw-r--r--fw/esp32/components/eos/include/drv2605l.h3
-rw-r--r--fw/esp32/components/eos/include/eos.h23
-rw-r--r--fw/esp32/components/eos/include/gsm.h117
-rw-r--r--fw/esp32/components/eos/include/i2c.h9
-rw-r--r--fw/esp32/components/eos/include/msgq.h32
-rw-r--r--fw/esp32/components/eos/include/net.h34
-rw-r--r--fw/esp32/components/eos/include/power.h20
-rw-r--r--fw/esp32/components/eos/include/sock.h18
l---------fw/esp32/components/eos/include/unicode.h1
-rw-r--r--fw/esp32/components/eos/include/wifi.h12
-rw-r--r--fw/esp32/components/eos/msgq.c69
-rw-r--r--fw/esp32/components/eos/net.c285
-rw-r--r--fw/esp32/components/eos/power.c325
-rw-r--r--fw/esp32/components/eos/sock.c163
l---------fw/esp32/components/eos/unicode.c1
-rwxr-xr-xfw/esp32/components/eos/wifi.c302
-rw-r--r--fw/esp32/main/app_main.c38
-rw-r--r--fw/esp32/main/component.mk8
38 files changed, 4060 insertions, 0 deletions
diff --git a/fw/esp32/LICENSE b/fw/esp32/LICENSE
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/fw/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/fw/esp32/Makefile b/fw/esp32/Makefile
new file mode 100644
index 0000000..eb26385
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/at_cmd.c b/fw/esp32/components/eos/at_cmd.c
new file mode 100644
index 0000000..4712ad7
--- /dev/null
+++ b/fw/esp32/components/eos/at_cmd.c
@@ -0,0 +1,164 @@
+#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[EOS_CELL_UART_SIZE_BUF];
+
+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);
+ }
+ ESP_LOGD(TAG, "URC Processed: %s", urc);
+ return 1;
+ }
+
+ ESP_LOGD(TAG, "URC NOT Processed: %s", urc);
+ 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;
+}
+
+void at_cmd(char *cmd) {
+ eos_modem_write(cmd, strlen(cmd));
+ ESP_LOGD(TAG, "Cmd: %s", cmd);
+}
+
+int at_expect(char *str_ok, char *str_err, uint32_t timeout) {
+ 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);
+ ESP_LOGD(TAG, "Expect: %s", at_buf);
+
+ if (at_buf[0] != '\0') {
+ if (!rv && str_ok && (regexec(&re_ok, at_buf, 0, NULL, 0) == 0)) return 1;
+ if (!rv && str_err && (regexec(&re_err, at_buf, 0, NULL, 0) == 0)) return 0;
+
+ at_urc_process(at_buf);
+ }
+
+ e = (uint32_t)(esp_timer_get_time() - t_start) / 1000;
+ if (e > timeout) return EOS_ERR_TIMEOUT;
+ } while (1);
+}
diff --git a/fw/esp32/components/eos/bq25895.c b/fw/esp32/components/eos/bq25895.c
new file mode 100644
index 0000000..0d1bb8d
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/cell.c b/fw/esp32/components/eos/cell.c
new file mode 100644
index 0000000..c2e03e1
--- /dev/null
+++ b/fw/esp32/components/eos/cell.c
@@ -0,0 +1,68 @@
+#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;
+
+ if (size < 1) return;
+ mtype = buffer[0];
+ switch (mtype & EOS_CELL_MTYPE_MASK) {
+ case EOS_CELL_MTYPE_DEV:
+ switch (mtype) {
+ case EOS_CELL_MTYPE_UART_DATA:
+ if (eos_modem_get_mode() == EOS_CELL_UART_MODE_RELAY) eos_modem_write(buffer+1, size-1);
+ break;
+
+ case EOS_CELL_MTYPE_UART_TAKE:
+ cell_mode = eos_modem_get_mode();
+ eos_modem_set_mode(EOS_CELL_UART_MODE_RELAY);
+ break;
+
+ case EOS_CELL_MTYPE_UART_GIVE:
+ eos_modem_set_mode(cell_mode);
+ break;
+
+ case EOS_CELL_MTYPE_PCM_DATA:
+ eos_cell_pcm_push(buffer+1, size-1);
+ break;
+
+ case EOS_CELL_MTYPE_PCM_START:
+ eos_cell_pcm_start();
+ break;
+
+ case EOS_CELL_MTYPE_PCM_STOP:
+ eos_cell_pcm_stop();
+ break;
+ }
+ break;
+
+ case EOS_CELL_MTYPE_VOICE:
+ eos_cell_voice_handler(mtype & ~EOS_CELL_MTYPE_MASK, buffer, size);
+ break;
+
+ case EOS_CELL_MTYPE_SMS:
+ eos_cell_sms_handler(mtype & ~EOS_CELL_MTYPE_MASK, buffer, size);
+ break;
+
+ case EOS_CELL_MTYPE_USSD:
+ eos_cell_ussd_handler(mtype & ~EOS_CELL_MTYPE_MASK, buffer, size);
+ break;
+
+ case EOS_CELL_MTYPE_DATA:
+ eos_cell_data_handler(mtype & ~EOS_CELL_MTYPE_MASK, buffer, size);
+ break;
+ }
+}
+
+void eos_cell_init(void) {
+ eos_net_set_handler(EOS_NET_MTYPE_CELL, cell_handler);
+}
+
diff --git a/fw/esp32/components/eos/cell_data.c b/fw/esp32/components/eos/cell_data.c
new file mode 100644
index 0000000..6732346
--- /dev/null
+++ b/fw/esp32/components/eos/cell_data.c
@@ -0,0 +1,20 @@
+#include <stdlib.h>
+
+#include <esp_log.h>
+
+#include "eos.h"
+#include "cell.h"
+
+void eos_cell_data_handler(unsigned char mtype, unsigned char *buffer, uint16_t size) {
+ int rv;
+
+ rv = eos_modem_take(1000);
+ if (rv) return;
+
+ buffer += 1;
+ size -= 1;
+ switch (mtype) {
+ }
+
+ eos_modem_give();
+}
diff --git a/fw/esp32/components/eos/cell_modem.c b/fw/esp32/components/eos/cell_modem.c
new file mode 100644
index 0000000..c6d718f
--- /dev/null
+++ b/fw/esp32/components/eos/cell_modem.c
@@ -0,0 +1,767 @@
+#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_IO_BUF 1024
+
+#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 AT_CMD_INIT_SIZE 3
+
+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
+#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
+
+static const char *TAG = "EOS MODEM";
+
+static char *at_cmd_init[AT_CMD_INIT_SIZE] = {
+ "AT+CFGRI=1\r",
+ "AT+CSCLK=1\r",
+ "AT+CMGF=0\r"
+};
+
+static SemaphoreHandle_t mutex;
+
+static QueueHandle_t modem_queue;
+static QueueHandle_t uart_queue;
+
+static char urc_buf[EOS_CELL_UART_SIZE_BUF];
+static char uart_buf[EOS_CELL_UART_SIZE_BUF];
+static size_t 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_UART_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 i, r;
+ int rv = EOS_OK;
+
+ rv = eos_modem_take(1000);
+ if (rv) return rv;
+
+ do {
+ at_cmd("AT\r");
+ 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) {
+ at_cmd("AT&F\r");
+ r = at_expect("^AT&F", NULL, 1000);
+ r = at_expect("^OK", NULL, 1000);
+ } else {
+ at_cmd("AT&F\r");
+ r = at_expect("^OK", NULL, 1000);
+
+ }
+ at_cmd("ATE0\r");
+ r = at_expect("^ATE0", NULL, 1000);
+ r = at_expect("^OK", "^ERROR", 1000);
+
+ for (i=0; i<AT_CMD_INIT_SIZE; i++) {
+ at_cmd(at_cmd_init[i]);
+ 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_end;
+ int rd = 0;
+
+ do {
+ char *uart_curr = uart_buf + uart_buf_len;
+ int _rd = eos_modem_read(uart_curr, MIN(bsize - rd, sizeof(uart_buf) - uart_buf_len - 1), 100);
+
+ rd += _rd;
+ uart_buf_len += _rd;
+ uart_buf[uart_buf_len] = '\0';
+ while ((ln_end = strchr(uart_curr, '\n'))) {
+ size_t urc_buf_len = ln_end - uart_buf;
+
+ if ((ln_end > uart_buf) && (*(ln_end - 1) == '\r')) urc_buf_len--;
+ memcpy(urc_buf, uart_buf, urc_buf_len);
+ urc_buf[urc_buf_len] = '\0';
+
+ uart_buf_len -= ln_end - uart_buf + 1;
+ if (uart_buf_len) memmove(uart_buf, ln_end + 1, uart_buf_len);
+ uart_curr = uart_buf;
+ uart_buf[uart_buf_len] = '\0';
+
+ at_urc_process(urc_buf);
+ }
+ if (uart_buf_len == sizeof(uart_buf) - 1) {
+ uart_buf_len = 0;
+ memcpy(urc_buf, uart_buf, sizeof(urc_buf));
+ at_urc_process(urc_buf);
+ }
+ } 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);
+ at_cmd("+++");
+ 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--;
+ at_cmd("+++");
+ t_start = esp_timer_get_time();
+ }
+ }
+ } while (!done);
+
+ return rv;
+}
+
+static int ppp_resume(void) {
+ int r;
+ int rv = EOS_OK;
+
+ at_cmd("ATO\r");
+ 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;
+ }
+
+ at_cmd(cmd);
+ r = at_expect("^OK", "^ERROR", 1000);
+ if (r <= 0) {
+ modem_set_mode(uart_mode);
+ xSemaphoreGive(uart_mutex);
+ return EOS_ERR;
+ }
+
+ at_cmd("AT+CGDATA=\"PPP\",1\r");
+ 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;
+
+ at_cmd("ATH\r");
+ 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_IO_BUF, UART_SIZE_IO_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) {
+ char *ln_end = NULL;
+ size_t buf_len = 0;
+ uint64_t t_start = esp_timer_get_time();
+
+ buf[0] = '\0';
+ if (uart_buf_len) {
+ buf_len = MIN(buf_size -1, uart_buf_len);
+ memcpy(buf, uart_buf, buf_len);
+ buf[buf_len] = '\0';
+ ln_end = strchr(buf, '\n');
+
+ uart_buf_len -= buf_len;
+ if (uart_buf_len) memmove(uart_buf, uart_buf + buf_len, uart_buf_len);
+ }
+
+ while (ln_end == NULL) {
+ int len;
+
+ if (buf_len == buf_size - 1) return EOS_ERR_FULL;
+ if (timeout && ((uint32_t)((esp_timer_get_time() - t_start) / 1000) > timeout)) return EOS_ERR_TIMEOUT;
+
+ len = eos_modem_read(buf + buf_len, MIN(buf_size - buf_len - 1, sizeof(uart_buf) - uart_buf_len), 10);
+ if (len > 0) {
+ buf[buf_len + len] = '\0';
+ ln_end = strchr(buf + buf_len, '\n');
+ buf_len += len;
+ }
+ }
+ buf_len -= ln_end - buf + 1;
+ if (buf_len) {
+ if (uart_buf_len) memmove(uart_buf + buf_len, uart_buf, uart_buf_len);
+ memcpy(uart_buf, ln_end + 1, buf_len);
+ uart_buf_len += buf_len;
+ }
+
+ if ((ln_end > buf) && (*(ln_end - 1) == '\r')) ln_end--;
+ *ln_end = '\0';
+
+ return EOS_OK;
+}
+
+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/fw/esp32/components/eos/cell_pcm.c b/fw/esp32/components/eos/cell_pcm.c
new file mode 100644
index 0000000..5c59643
--- /dev/null
+++ b/fw/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_PCM_DATA;
+ bytes_r = eos_cell_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_PCM_DATA;
+ }
+ if (1 + hold_bytes_r + PCM_MIC_WM <= EOS_NET_SIZE_BUF) hold_bytes_r += eos_cell_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_cell_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_cell_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;
+}
+
+static ssize_t 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_cell_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 = 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_cell_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_cell_pcm_stop(void) {
+ i2s_stop(I2S_NUM_0);
+}
diff --git a/fw/esp32/components/eos/cell_sms.c b/fw/esp32/components/eos/cell_sms.c
new file mode 100644
index 0000000..05acc50
--- /dev/null
+++ b/fw/esp32/components/eos/cell_sms.c
@@ -0,0 +1,22 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <esp_log.h>
+
+#include "eos.h"
+#include "cell.h"
+
+
+void eos_cell_sms_handler(unsigned char mtype, unsigned char *buffer, uint16_t size) {
+ int rv;
+
+ rv = eos_modem_take(1000);
+ if (rv) return;
+
+ buffer += 1;
+ size -= 1;
+ switch (mtype) {
+ }
+
+ eos_modem_give();
+}
diff --git a/fw/esp32/components/eos/cell_ussd.c b/fw/esp32/components/eos/cell_ussd.c
new file mode 100644
index 0000000..2daa00f
--- /dev/null
+++ b/fw/esp32/components/eos/cell_ussd.c
@@ -0,0 +1,34 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <esp_log.h>
+
+#include "at_cmd.h"
+#include "cell.h"
+#include "gsm.h"
+
+static char cmd[256];
+
+void eos_cell_ussd_handler(unsigned char mtype, unsigned char *buffer, uint16_t size) {
+ int cmd_len, rv;
+
+ rv = eos_modem_take(1000);
+ if (rv) return;
+
+ buffer += 1;
+ size -= 1;
+ switch (mtype) {
+ case EOS_CELL_MTYPE_USSD_REQUEST:
+ if (size == 0) return;
+
+ buffer[size] = '\0';
+ cmd_len = snprintf(cmd, sizeof(cmd), "AT+CUSD=1,\"%s\",15\r", buffer);
+ if ((cmd_len < 0) || (cmd_len >= sizeof(cmd))) return;
+ at_cmd(cmd);
+
+ break;
+ }
+
+ eos_modem_give();
+}
+
diff --git a/fw/esp32/components/eos/cell_voice.c b/fw/esp32/components/eos/cell_voice.c
new file mode 100644
index 0000000..3f6a2a5
--- /dev/null
+++ b/fw/esp32/components/eos/cell_voice.c
@@ -0,0 +1,20 @@
+#include <stdlib.h>
+
+#include <esp_log.h>
+
+#include "eos.h"
+#include "cell.h"
+
+void eos_cell_voice_handler(unsigned char mtype, unsigned char *buffer, uint16_t size) {
+ int rv;
+
+ rv = eos_modem_take(1000);
+ if (rv) return;
+
+ buffer += 1;
+ size -= 1;
+ switch (mtype) {
+ }
+
+ eos_modem_give();
+}
diff --git a/fw/esp32/components/eos/component.mk b/fw/esp32/components/eos/component.mk
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fw/esp32/components/eos/component.mk
diff --git a/fw/esp32/components/eos/drv2605l.c b/fw/esp32/components/eos/drv2605l.c
new file mode 100644
index 0000000..3944289
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/gsm.c b/fw/esp32/components/eos/gsm.c
new file mode 100644
index 0000000..788722e
--- /dev/null
+++ b/fw/esp32/components/eos/gsm.c
@@ -0,0 +1,453 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "gsm.h"
+
+#define DIVC(x,y) ((x) / (y) + ((x) % (y) != 0))
+
+uint8_t pdu_getc(char *pdu) {
+ int ch;
+ sscanf(pdu, "%2X", &ch);
+ return ch;
+}
+
+void pdu_putc(uint8_t ch, char *pdu) {
+ sprintf(pdu, "%.2X", ch);
+}
+
+void pdu_puts(uint8_t *s, int s_len, char *pdu) {
+ int i;
+
+ for (i=0; i<s_len; i++) {
+ sprintf(pdu + 2 * i, "%.2X", s[i]);
+ }
+}
+
+void pdu_gets(char *pdu, uint8_t *s, int s_len) {
+ int i, ch;
+
+ for (i=0; i<s_len; i++) {
+ sscanf(pdu + 2 * i, "%2X", &ch);
+ s[i] = ch;
+ }
+}
+
+void gsm_dcs_dec(uint8_t dcs, uint8_t *enc, uint16_t *flags) {
+ if ((dcs & GSM_DCS_GENERAL_IND) == 0) {
+ *enc = dcs & GSM_DCS_ENC;
+ if (dcs & GSM_DCS_CLASS_IND) {
+ *flags |= GSM_FLAG_CLASS;
+ *flags |= (uint16_t)(dcs & GSM_DCS_CLASS) << 8;
+ }
+ if (dcs & GSM_DCS_COMPRESS_IND) *flags |= GSM_FLAG_COMPRESS;
+ if (dcs & GSM_DCS_DELETE_IND) *flags |= GSM_FLAG_DELETE;
+ } else {
+ uint8_t group = dcs & GSM_DCS_GROUP;
+
+ switch (group) {
+ case GSM_DCS_MWI_DISCARD:
+ case GSM_DCS_MWI_STORE_GSM7:
+ case GSM_DCS_MWI_STORE_UCS2:
+ if (group == GSM_DCS_MWI_STORE_UCS2) {
+ *enc = GSM_ENC_UCS2;
+ } else {
+ *enc = GSM_ENC_7BIT;
+ }
+ if (GSM_DCS_MWI_DISCARD) *flags |= GSM_FLAG_DISCARD;
+ *flags |= GSM_FLAG_MWI;
+ *flags |= (uint16_t)(dcs & (GSM_DCS_MWI_SENSE | GSM_DCS_MWI_TYPE)) << 12;
+ break;
+
+ case GSM_DCS_ENCLASS:
+ *flags |= GSM_FLAG_CLASS;
+ *flags |= (uint16_t)(dcs & GSM_DCS_CLASS) << 8;
+ *enc = dcs & GSM_DCS_ENCLASS_ENC ? GSM_ENC_8BIT : GSM_ENC_7BIT;
+ break;
+ }
+ }
+}
+
+void gsm_dcs_enc(uint8_t enc, uint16_t flags, uint8_t *dcs) {
+ *dcs = enc;
+ if (flags & GSM_FLAG_CLASS) {
+ *dcs |= GSM_DCS_CLASS_IND;
+ *dcs |= (flags >> 8) & GSM_DCS_CLASS;
+ }
+ if (flags & GSM_FLAG_COMPRESS) *dcs |= GSM_DCS_COMPRESS_IND;
+ if (flags & GSM_FLAG_DELETE) *dcs |= GSM_DCS_DELETE_IND;
+}
+
+int gsm_ts_enc(char *ts, char *pdu, int pdu_size) {
+ uint8_t tz;
+ int tz_hh, tz_mm;
+
+ if (pdu_size < 14) return GSM_ERR;
+
+ pdu[1] = ts[2]; // YY
+ pdu[0] = ts[3];
+
+ pdu[3] = ts[5]; // MM
+ pdu[2] = ts[6];
+
+ pdu[5] = ts[8]; // DD
+ pdu[4] = ts[9];
+
+ pdu[7] = ts[11]; // hh
+ pdu[6] = ts[12];
+
+ pdu[9] = ts[14]; // mm
+ pdu[8] = ts[15];
+
+ pdu[11] = ts[17]; // ss
+ pdu[10] = ts[18];
+
+ sscanf(ts + 20, "%2d:%2d", &tz_hh, &tz_mm);
+ tz = tz_hh * 4 + tz_mm / 15;
+ tz = (tz / 10) | ((tz % 10) << 4);
+ if (ts[19] == '-') tz |= 0x08;
+
+ pdu_putc(tz, pdu + 12);
+
+ return 14;
+}
+
+int gsm_ts_dec(char *pdu, int pdu_len, char *ts) {
+ uint8_t tz;
+
+ if (pdu_len < 14) return GSM_ERR;
+
+ ts[0] = '2';
+ ts[1] = '0';
+ ts[2] = pdu[1]; // YY
+ ts[3] = pdu[0];
+ ts[4] = '-';
+ ts[5] = pdu[3]; // MM
+ ts[6] = pdu[2];
+ ts[7] = '-';
+ ts[8] = pdu[5]; // DD
+ ts[9] = pdu[4];
+ ts[10] = 'T';
+ ts[11] = pdu[7]; // hh
+ ts[12] = pdu[6];
+ ts[13] = ':';
+ ts[14] = pdu[9]; // mm
+ ts[15] = pdu[8];
+ ts[16] = ':';
+ ts[17] = pdu[11]; // ss
+ ts[18] = pdu[10];
+
+ tz = pdu_getc(pdu + 12);
+ if (tz & 0x08) {
+ ts[19] = '-';
+ tz = tz & ~0x08;
+ } else {
+ ts[19] = '+';
+ }
+ tz = (tz & 0x0f) * 10 + (tz >> 4);
+ sprintf(ts + 20, "%.2d:%.2d", tz / 4, (tz % 4) * 15);
+
+ return 14;
+}
+
+int gsm_7bit_enc(char *text, int text_len, char *pdu, int padb) {
+ uint8_t carry = 0;
+ int i = 0, pdu_len = 0, shc = 0;
+
+ if (!text_len) return 0;
+
+ if (padb) {
+ shc = 7 - padb;
+ } else {
+ carry = *text;
+ i++;
+ }
+
+ while (i < text_len) {
+ pdu_putc(carry | (*(text + i) << (7 - shc)), pdu + pdu_len);
+ pdu_len += 2;
+
+ shc++;
+ shc = shc % 7;
+ if (!shc) {
+ i++;
+ if (i == text_len) return pdu_len;
+ }
+
+ carry = *(text + i) >> shc;
+ i++;
+ }
+ pdu_putc(carry, pdu + pdu_len);
+ pdu_len += 2;
+
+ return pdu_len;
+}
+
+int gsm_7bit_dec(char *pdu, char *text, int text_len, int padb) {
+ uint8_t ch;
+ uint8_t carry = 0;
+ int i = 0, pdu_len = 0, shc = 0;
+
+ if (!text_len) return 0;
+
+ if (padb) {
+ ch = pdu_getc(pdu);
+ pdu_len += 2;
+ if (padb == 1) {
+ *text = ch >> 1;
+ i++;
+ } else {
+ carry = ch >> padb;
+ shc = 8 - padb;
+ }
+ }
+
+ while (i < text_len) {
+ ch = pdu_getc(pdu + pdu_len);
+ pdu_len += 2;
+
+ *(text + i) = ((ch << shc) | carry) & 0x7f;
+ carry = ch >> (7 - shc);
+ i++;
+
+ shc++;
+ shc = shc % 7;
+ if (!shc && (i < text_len)) {
+ *(text + i) = carry;
+ carry = 0;
+ i++;
+ }
+ }
+
+ return pdu_len;
+}
+
+int gsm_addr_enc(char *addr, int addr_len, uint8_t addr_type, char *pdu, int pdu_size) {
+ int _pdu_len;
+
+ addr_type |= GSM_EXT;
+
+ if ((addr_type & GSM_TON) == GSM_TON_ALPHANUMERIC) {
+ int _addr_len = DIVC(addr_len * 7, 4);
+
+ _pdu_len = 4 + DIVC(_addr_len, 2) * 2;
+ if (pdu_size < _pdu_len) return GSM_ERR;
+
+ pdu_putc(_addr_len, pdu);
+ pdu_putc(addr_type, pdu + 2);
+ gsm_7bit_enc(addr, addr_len, pdu, 0);
+ } else {
+ int i;
+
+ if (addr_type & GSM_TON_INTERNATIONAL) {
+ if (addr[0] != '+') return GSM_ERR;
+ addr++;
+ addr_len--;
+ }
+ _pdu_len = 4 + DIVC(addr_len, 2) * 2;
+ if (pdu_size < _pdu_len) return GSM_ERR;
+
+ pdu_putc(addr_len, pdu);
+ pdu_putc(addr_type, pdu + 2);
+ for (i=0; i<addr_len / 2; i++) {
+ pdu[4 + 2 * i] = addr[2 * i + 1];
+ pdu[4 + 2 * i + 1] = addr[2 * i];
+ }
+ if (addr_len % 2 != 0) {
+ pdu[4 + 2 * i] = 'F';
+ pdu[4 + 2 * i + 1] = addr[2 * i];
+ }
+ }
+
+ return _pdu_len;
+}
+
+int gsm_addr_dec(char *pdu, int pdu_len, char *addr, int addr_size, int *addr_len, uint8_t *addr_type) {
+ int _pdu_len;
+
+ if (pdu_len < 4) return GSM_ERR;
+
+ *addr_len = pdu_getc(pdu);
+ *addr_type = pdu_getc(pdu + 2);
+
+ if (!(*addr_type & GSM_EXT)) return GSM_ERR;
+
+ _pdu_len = 4 + DIVC(*addr_len, 2) * 2;
+ if (pdu_len < _pdu_len) return GSM_ERR;
+
+ if ((*addr_type & GSM_TON) == GSM_TON_ALPHANUMERIC) {
+ *addr_len = (*addr_len * 4) / 7;
+ if (addr_size < *addr_len) return GSM_ERR;
+
+ gsm_7bit_dec(pdu + 4, addr, *addr_len, 0);
+ } else {
+ int i;
+ int _addr_len = *addr_len;
+
+ if (*addr_type & GSM_TON_INTERNATIONAL) {
+ addr[0] = '+';
+ addr++;
+ (*addr_len)++;
+ }
+ if (addr_size < *addr_len) return GSM_ERR;
+
+ for (i=0; i<_addr_len / 2; i++) {
+ addr[2 * i] = pdu[4 + 2 * i + 1];
+ addr[2 * i + 1] = pdu[4 + 2 * i];
+ }
+ if (_addr_len % 2 != 0) {
+ addr[2 * i] = pdu[4 + 2 * i + 1];
+ }
+ }
+
+ return _pdu_len;
+}
+
+int gsm_sms_enc(uint8_t pid, char *addr, int addr_len, uint8_t addr_type, uint8_t *udh, int udh_len, uint8_t *msg, int msg_len, uint8_t enc, uint16_t flags, char *pdu, int pdu_size) {
+ int rv, _pdu_len = 0;
+ uint8_t mti = GSM_MTI_SUBMIT;
+ uint8_t dcs = 0;
+ uint8_t udl;
+
+ if (udh_len) mti |= GSM_UDHI;
+
+ if (pdu_size < 4) return GSM_ERR;
+ pdu_putc(mti, pdu);
+ pdu_putc(00, pdu + 2);
+ _pdu_len += 4;
+
+ rv = gsm_addr_enc(addr, addr_len, addr_type, pdu + _pdu_len, pdu_size - _pdu_len);
+ if (rv < 0) return rv;
+ _pdu_len += rv;
+
+ if (pdu_size < _pdu_len + 4) return GSM_ERR;
+ gsm_dcs_enc(enc, flags, &dcs);
+ pdu_putc(pid, pdu + _pdu_len);
+ pdu_putc(dcs, pdu + _pdu_len + 2);
+ _pdu_len += 4;
+
+ if (enc == GSM_ENC_7BIT) {
+ int udh_blen = 0;
+ int padb = 0;
+
+ if (udh_len) {
+ udh_blen = 8 * (udh_len + 1);
+ padb = DIVC(udh_blen, 7) * 7 - udh_blen;
+ }
+ udl = DIVC(udh_blen, 7) + msg_len;
+
+ if (pdu_size < _pdu_len + (DIVC(udl * 7, 8) + 1) * 2) return GSM_ERR;
+ pdu_putc(udl, pdu + _pdu_len);
+ _pdu_len += 2;
+
+ if (udh_len) {
+ pdu_putc(udh_len, pdu + _pdu_len);
+ pdu_puts(udh, udh_len, pdu + _pdu_len + 2);
+ _pdu_len += (udh_len + 1) * 2;
+ }
+
+ rv = gsm_7bit_enc((char *)msg, msg_len, pdu + _pdu_len, padb);
+ if (rv < 0) return rv;
+ _pdu_len += rv;
+ } else {
+ udl = msg_len + (udh_len ? udh_len + 1 : 0);
+
+ if (pdu_size < _pdu_len + (udl + 1) * 2) return GSM_ERR;
+ pdu_putc(udl, pdu + _pdu_len);
+ _pdu_len += 2;
+
+ if (udh_len) {
+ pdu_putc(udh_len, pdu + _pdu_len);
+ pdu_puts(udh, udh_len, pdu + _pdu_len + 2);
+ _pdu_len += (udh_len + 1) * 2;
+ }
+
+ pdu_puts(msg, msg_len, pdu + _pdu_len);
+ _pdu_len += msg_len * 2;
+ }
+
+ return _pdu_len;
+}
+
+int gsm_sms_dec(char *pdu, int pdu_len, uint8_t *pid, char *addr, int addr_size, int *addr_len, uint8_t *addr_type, uint8_t *udh, int udh_size, int *udh_len, uint8_t *msg, int msg_size, int *msg_len, char *ts, uint8_t *enc, uint16_t *flags) {
+ int rv, _pdu_len = 0;
+ uint8_t mti;
+ uint8_t dcs;
+ uint8_t udl;
+
+ if (pdu_len < 2) return GSM_ERR;
+ mti = pdu_getc(pdu);
+ _pdu_len += 2;
+ if ((mti & GSM_MTI) != GSM_MTI_DELIVER) return GSM_ERR;
+
+ rv = gsm_addr_dec(pdu + _pdu_len, pdu_len - _pdu_len, addr, addr_size, addr_len, addr_type);
+ if (rv < 0) return rv;
+ _pdu_len += rv;
+
+ if (pdu_len < _pdu_len + 4) return GSM_ERR;
+ *pid = pdu_getc(pdu + _pdu_len);
+ dcs = pdu_getc(pdu + _pdu_len + 2);
+ _pdu_len += 4;
+ gsm_dcs_dec(dcs, enc, flags);
+
+ rv = gsm_ts_dec(pdu + _pdu_len, pdu_len - _pdu_len, ts);
+ if (rv < 0) return rv;
+ _pdu_len += rv;
+
+ if (pdu_len < _pdu_len + 2) return GSM_ERR;
+ udl = pdu_getc(pdu + _pdu_len);
+ _pdu_len += 2;
+
+ if ((mti & GSM_UDHI) && (udl == 0)) return GSM_ERR;
+ *udh_len = 0;
+
+ if (*enc == GSM_ENC_7BIT) {
+ int udh_blen = 0;
+ int padb = 0;
+
+ if (pdu_len < _pdu_len + DIVC(udl * 7, 8) * 2) return GSM_ERR;
+
+ if (mti & GSM_UDHI) {
+ *udh_len = pdu_getc(pdu + _pdu_len);
+ udh_blen = 8 * (*udh_len + 1);
+ padb = DIVC(udh_blen, 7) * 7 - udh_blen;
+
+ if (udl * 7 < udh_blen) return GSM_ERR;
+ if (udh_size < *udh_len) return GSM_ERR;
+
+ pdu_gets(pdu + _pdu_len + 2, udh, *udh_len);
+ _pdu_len += (*udh_len + 1) * 2;
+ } else {
+ *udh_len = 0;
+ }
+
+ *msg_len = udl - DIVC(udh_blen, 7);
+ if (msg_size < *msg_len) return GSM_ERR;
+
+ rv = gsm_7bit_dec(pdu + _pdu_len, (char *)msg, *msg_len, padb);
+ if (rv < 0) return rv;
+ _pdu_len += rv;
+ } else {
+ if (pdu_len < _pdu_len + udl * 2) return GSM_ERR;
+
+ if (mti & GSM_UDHI) {
+ *udh_len = pdu_getc(pdu + _pdu_len);
+
+ if (udl < *udh_len + 1) return GSM_ERR;
+ if (udh_size < *udh_len) return GSM_ERR;
+
+ pdu_gets(pdu + _pdu_len + 2, udh, *udh_len);
+ _pdu_len += (*udh_len + 1) * 2;
+ } else {
+ *udh_len = 0;
+ }
+
+ *msg_len = udl - (*udh_len ? *udh_len + 1 : 0);
+ if (msg_size < *msg_len) return GSM_ERR;
+
+ pdu_gets(pdu + _pdu_len, msg, *msg_len);
+ _pdu_len += *msg_len * 2;
+ }
+
+ return _pdu_len;
+}
diff --git a/fw/esp32/components/eos/gsm_cp.c b/fw/esp32/components/eos/gsm_cp.c
new file mode 100644
index 0000000..3ab98c5
--- /dev/null
+++ b/fw/esp32/components/eos/gsm_cp.c
@@ -0,0 +1,109 @@
+#include <stdint.h>
+
+#include "gsm.h"
+
+#define UCS2_SUPL_SIZE 11
+
+static const uint16_t gsm7_to_ucs2[128] = {
+ 0x0040, 0x00a3, 0x0024, 0x00a5, 0x00e8, 0x00e9, 0x00f9, 0x00ec, 0x00f2, 0x00c7, 0x000a, 0x00d8, 0x00f8, 0x000d, 0x00c5, 0x00e5,
+ 0x0394, 0x005f, 0x03a6, 0x0393, 0x039b, 0x03a9, 0x03a0, 0x03a8, 0x03a3, 0x0398, 0x039e, 0x001b, 0x00c6, 0x00e6, 0x00df, 0x00c9,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x00a4, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x00a1, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00c4, 0x00d6, 0x00d1, 0x00dc, 0x00a7,
+ 0x00bf, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00e4, 0x00f6, 0x00f1, 0x00fc, 0x00e0
+};
+
+static const uint16_t gsm7e_to_ucs2[128] = {
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x000c, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0x005e, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x007b, 0x007d, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x005c,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x005b, 0x007e, 0x005d, 0xffff,
+ 0x007c, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x20ac, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
+};
+
+static const uint16_t ucs2_to_gsm7[256] = {
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x000a, 0xffff, 0x1b0a, 0x000d, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x001b, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0002, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0000, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x1b3c, 0x1b2f, 0x1b3e, 0x1b14, 0x0011,
+ 0xffff, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x1b28, 0x1b40, 0x1b29, 0x1b3d, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0x0040, 0xffff, 0x0001, 0x0024, 0x0003, 0xffff, 0x005f, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0060,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0x005b, 0x000e, 0x001c, 0x0009, 0xffff, 0x001f, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0x005d, 0xffff, 0xffff, 0xffff, 0xffff, 0x005c, 0xffff, 0x000b, 0xffff, 0xffff, 0xffff, 0x005e, 0xffff, 0xffff, 0x001e,
+ 0x007f, 0xffff, 0xffff, 0xffff, 0x007b, 0x000f, 0x001d, 0xffff, 0x0004, 0x0005, 0xffff, 0xffff, 0x0007, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0x007d, 0x0008, 0xffff, 0xffff, 0xffff, 0x007c, 0xffff, 0x000c, 0x0006, 0xffff, 0xffff, 0x007e, 0xffff, 0xffff, 0xffff
+};
+
+static const uint16_t ucs2_to_gsm7_supl[UCS2_SUPL_SIZE][2] = {
+ {0x0394, 0x10},
+ {0x03a6, 0x12},
+ {0x0393, 0x13},
+ {0x039b, 0x14},
+ {0x03a9, 0x15},
+ {0x03a0, 0x16},
+ {0x03a8, 0x17},
+ {0x03a3, 0x18},
+ {0x0398, 0x19},
+ {0x039e, 0x1a},
+ {0x20ac, 0x1b65}
+};
+
+int gsm_ucs2_to_7bit(uint16_t ucs2, char *gsm7, int gsm7_size) {
+ uint16_t ch = 0xffff;
+ int ret = 0;
+
+ if (gsm7_size < 1) return GSM_ERR;
+
+ if (ucs2 < 256) {
+ ch = ucs2_to_gsm7[ucs2];
+ } else {
+ int i;
+
+ for (i=0; i<UCS2_SUPL_SIZE; i++) {
+ if (ucs2_to_gsm7_supl[i][0] == ucs2) {
+ ch = ucs2_to_gsm7_supl[i][1];
+ break;
+ }
+ }
+ }
+ if (ch == 0xffff) return GSM_ERR;
+ if (ch & 0xff00) {
+ if (gsm7_size < 2) return GSM_ERR;
+ *gsm7 = 0x1b;
+ gsm7++;
+ ret++;
+ }
+ *gsm7 = ch & 0x7f;
+ ret++;
+
+ return ret;
+}
+
+int gsm_7bit_to_ucs2(char *gsm7, int gsm7_len, uint16_t *ucs2) {
+ int ret;
+
+ if (gsm7_len < 1) return GSM_ERR;
+ if (*gsm7 != 0x1b) {
+ *ucs2 = gsm7_to_ucs2[*gsm7 & 0x7f];
+ ret = 1;
+ } else {
+ if (gsm7_len < 2) return GSM_ERR;
+ gsm7++;
+ ret = 2;
+ *ucs2 = gsm7e_to_ucs2[*gsm7 & 0x7f];
+ }
+ if (*ucs2 == 0xffff) return GSM_ERR;
+
+ return ret;
+}
diff --git a/fw/esp32/components/eos/i2c.c b/fw/esp32/components/eos/i2c.c
new file mode 100644
index 0000000..5b8fcc7
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/include/_net.h b/fw/esp32/components/eos/include/_net.h
new file mode 100644
index 0000000..35b5308
--- /dev/null
+++ b/fw/esp32/components/eos/include/_net.h
@@ -0,0 +1 @@
+#include "net.h" \ No newline at end of file
diff --git a/fw/esp32/components/eos/include/at_cmd.h b/fw/esp32/components/eos/include/at_cmd.h
new file mode 100644
index 0000000..615a1c1
--- /dev/null
+++ b/fw/esp32/components/eos/include/at_cmd.h
@@ -0,0 +1,20 @@
+#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);
+void at_cmd(char *cmd);
+int at_expect(char *str_ok, char *str_err, uint32_t timeout);
diff --git a/fw/esp32/components/eos/include/bq25895.h b/fw/esp32/components/eos/include/bq25895.h
new file mode 100644
index 0000000..b5a7f92
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/include/cell.h b/fw/esp32/components/eos/include/cell.h
new file mode 100644
index 0000000..3bf6b32
--- /dev/null
+++ b/fw/esp32/components/eos/include/cell.h
@@ -0,0 +1,68 @@
+#include <sys/types.h>
+#include <stdint.h>
+
+#define EOS_CELL_MTYPE_DEV 0x00
+#define EOS_CELL_MTYPE_VOICE 0x10
+#define EOS_CELL_MTYPE_SMS 0x20
+#define EOS_CELL_MTYPE_CBS 0x30
+#define EOS_CELL_MTYPE_USSD 0x40
+#define EOS_CELL_MTYPE_DATA 0x70
+
+#define EOS_CELL_MTYPE_MASK 0xf0
+#define EOS_CELL_MAX_MTYPE 8
+
+#define EOS_CELL_MTYPE_READY 0
+#define EOS_CELL_MTYPE_UART_DATA 1
+#define EOS_CELL_MTYPE_UART_TAKE 2
+#define EOS_CELL_MTYPE_UART_GIVE 3
+#define EOS_CELL_MTYPE_PCM_DATA 4
+#define EOS_CELL_MTYPE_PCM_START 5
+#define EOS_CELL_MTYPE_PCM_STOP 6
+
+#define EOS_CELL_MTYPE_VOICE_DIAL 1
+#define EOS_CELL_MTYPE_VOICE_RING 2
+#define EOS_CELL_MTYPE_VOICE_ANSWER 3
+#define EOS_CELL_MTYPE_VOICE_HANGUP 4
+#define EOS_CELL_MTYPE_VOICE_BEGIN 5
+#define EOS_CELL_MTYPE_VOICE_END 6
+
+#define EOS_CELL_MTYPE_USSD_REQUEST 1
+#define EOS_CELL_MTYPE_USSD_REPLY 2
+
+#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
+
+#define EOS_CELL_UART_SIZE_BUF 128
+
+void eos_cell_init(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_pcm_init(void);
+ssize_t eos_cell_pcm_read(unsigned char *data, size_t size);
+int eos_cell_pcm_push(unsigned char *data, size_t size);
+void eos_cell_pcm_start(void);
+void eos_cell_pcm_stop(void);
+
+void eos_cell_voice_handler(unsigned char mtype, unsigned char *buffer, uint16_t size);
+void eos_cell_sms_handler(unsigned char mtype, unsigned char *buffer, uint16_t size);
+void eos_cell_ussd_handler(unsigned char mtype, unsigned char *buffer, uint16_t size);
+void eos_cell_data_handler(unsigned char mtype, unsigned char *buffer, uint16_t size);
diff --git a/fw/esp32/components/eos/include/drv2605l.h b/fw/esp32/components/eos/include/drv2605l.h
new file mode 100644
index 0000000..de222e4
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/include/eos.h b/fw/esp32/components/eos/include/eos.h
new file mode 100644
index 0000000..0e660fb
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/include/gsm.h b/fw/esp32/components/eos/include/gsm.h
new file mode 100644
index 0000000..2c4f7b4
--- /dev/null
+++ b/fw/esp32/components/eos/include/gsm.h
@@ -0,0 +1,117 @@
+#define GSM_OK 0
+#define GSM_ERR -1
+
+/* Message-Type-Indicator */
+#define GSM_MTI 0x03
+#define GSM_MTI_DELIVER 0x00
+#define GSM_MTI_DELIVER_REPORT 0x00
+#define GSM_MTI_SUBMIT 0x01
+#define GSM_MTI_SUBMIT_REPORT 0x01
+#define GSM_MTI_COMMAND 0x02
+#define GSM_MTI_COMMAND_REPORT 0x02
+
+#define GSM_MMS 0x04 /* More-Messages-to-Send */
+#define GSM_RD 0x04 /* Reject-Duplicates */
+#define GSM_LP 0x08 /* Loop-Prevention */
+
+/* Validity-Period-Format */
+#define GSM_VPF 0x18
+#define GSM_VPF_NONE 0x00
+#define GSM_VPF_ENHANCED 0x08
+#define GSM_VPF_RELATIVE 0x10
+#define GSM_VPF_ABSOLUTE 0x18
+
+#define GSM_SRI 0x20 /* Status-Report-Indication */
+#define GSM_SRR 0x20 /* Status-Report-Request */
+#define GSM_SRQ 0x20 /* Status-Report-Qualifier */
+#define GSM_UDHI 0x40 /* User-Data-Header-Indicator */
+#define GSM_RP 0x80 /* Reply-Path */
+
+/* Type-of-Number */
+#define GSM_TON 0x70
+#define GSM_TON_UNKNOWN 0x00
+#define GSM_TON_INTERNATIONAL 0x10
+#define GSM_TON_NATIONAL 0x20
+#define GSM_TON_NETWORK 0x30
+#define GSM_TON_SUBSCRIBER 0x40
+#define GSM_TON_ALPHANUMERIC 0x50
+#define GSM_TON_ABBRREVIATED 0x60
+
+/* Numbering-Plan-Identification */
+#define GSM_NPI 0x0f
+#define GSM_NPI_UNKNOWN 0x00
+#define GSM_NPI_TELEPHONE 0x01
+#define GSM_NPI_DATA 0x03
+#define GSM_NPI_TELEX 0x04
+#define GSM_NPI_SCS1 0x05
+#define GSM_NPI_SCS2 0x06
+#define GSM_NPI_NATIONAL 0x08
+#define GSM_NPI_PRIVATE 0x09
+#define GSM_NPI_ERMES 0x0a
+
+#define GSM_EXT 0x80
+
+/* Protocol-Identifier */
+#define GSM_PID_DEFAULT 0
+#define GSM_PID_TYPE0 64
+
+/* Data-Coding-Scheme */
+#define GSM_DCS_CLASS 0x03
+#define GSM_DCS_ENC 0x0c
+
+#define GSM_DCS_CLASS_IND 0x10
+#define GSM_DCS_COMPRESS_IND 0x20
+#define GSM_DCS_DELETE_IND 0x40
+#define GSM_DCS_GENERAL_IND 0x80
+#define GSM_DCS_GROUP 0xf0
+
+#define GSM_DCS_MWI_DISCARD 0xc0
+#define GSM_DCS_MWI_STORE_GSM7 0xd0
+#define GSM_DCS_MWI_STORE_UCS2 0xe0
+#define GSM_DCS_MWI_SENSE 0x08
+#define GSM_DCS_MWI_TYPE 0x03
+
+#define GSM_DCS_ENCLASS 0xf0
+#define GSM_DCS_ENCLASS_ENC 0x04
+
+/* Parameter-Indicator */
+#define GSM_PI_PID 0x01
+#define GSM_PI_DCS 0x02
+#define GSM_PI_UD 0x04
+#define GSM_PI_EXT 0x08
+
+/* character set */
+#define GSM_ENC_7BIT 0x00
+#define GSM_ENC_8BIT 0x04
+#define GSM_ENC_UCS2 0x08
+
+/* message waiting indication */
+#define GSM_MWI_TYPE_VOICEMAIL 0x00
+#define GSM_MWI_TYPE_FAX 0x01
+#define GSM_MWI_TYPE_EMAIL 0x02
+#define GSM_MWI_TYPE_OTHER 0x03
+
+/* flags */
+#define GSM_FLAG_COMPRESS 0x0001
+#define GSM_FLAG_DELETE 0x0002
+#define GSM_FLAG_DISCARD 0x0004
+
+/* message class */
+#define GSM_FLAG_CLASS 0x0400
+#define GSM_FLAG_CLASS0 0x0000 /* Flash */
+#define GSM_FLAG_CLASS1 0x0100 /* ME-specific */
+#define GSM_FLAG_CLASS2 0x0200 /* (U)SIM-specific */
+#define GSM_FLAG_CLASS4 0x0300 /* TE-specific */
+#define GSM_FLAG_CLASS_MASK 0x0f00
+
+/* message waiting indication */
+#define GSM_FLAG_MWI 0x4000
+#define GSM_FLAG_MWI_SENSE 0x8000
+#define GSM_FLAG_MWI_VOICEMAIL 0x0000
+#define GSM_FLAG_MWI_FAX 0x1000
+#define GSM_FLAG_MWI_EMAIL 0x2000
+#define GSM_FLAG_MWI_OTHER 0x3000
+#define GSM_FLAG_MWI_MASK 0xf000
+
+int gsm_7bit_enc(char *text, int text_len, char *pdu, int padb);
+int gsm_7bit_dec(char *pdu, char *text, int text_len, int padb);
diff --git a/fw/esp32/components/eos/include/i2c.h b/fw/esp32/components/eos/include/i2c.h
new file mode 100644
index 0000000..144f5e1
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/include/msgq.h b/fw/esp32/components/eos/include/msgq.h
new file mode 100644
index 0000000..86bb067
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/include/net.h b/fw/esp32/components/eos/include/net.h
new file mode 100644
index 0000000..54bad6d
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/include/power.h b/fw/esp32/components/eos/include/power.h
new file mode 100644
index 0000000..0a57b19
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/include/sock.h b/fw/esp32/components/eos/include/sock.h
new file mode 100644
index 0000000..7e937cb
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/include/unicode.h b/fw/esp32/components/eos/include/unicode.h
new file mode 120000
index 0000000..e859a65
--- /dev/null
+++ b/fw/esp32/components/eos/include/unicode.h
@@ -0,0 +1 @@
+../../../../fe310/eos/unicode.h \ No newline at end of file
diff --git a/fw/esp32/components/eos/include/wifi.h b/fw/esp32/components/eos/include/wifi.h
new file mode 100644
index 0000000..6009f7c
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/msgq.c b/fw/esp32/components/eos/msgq.c
new file mode 100644
index 0000000..c704399
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/net.c b/fw/esp32/components/eos/net.c
new file mode 100644
index 0000000..9a4a024
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/power.c b/fw/esp32/components/eos/power.c
new file mode 100644
index 0000000..f07e67b
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/sock.c b/fw/esp32/components/eos/sock.c
new file mode 100644
index 0000000..17357e4
--- /dev/null
+++ b/fw/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/fw/esp32/components/eos/unicode.c b/fw/esp32/components/eos/unicode.c
new file mode 120000
index 0000000..4cdffa7
--- /dev/null
+++ b/fw/esp32/components/eos/unicode.c
@@ -0,0 +1 @@
+../../../fe310/eos/unicode.c \ No newline at end of file
diff --git a/fw/esp32/components/eos/wifi.c b/fw/esp32/components/eos/wifi.c
new file mode 100755
index 0000000..3dd90ba
--- /dev/null
+++ b/fw/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/fw/esp32/main/app_main.c b/fw/esp32/main/app_main.c
new file mode 100644
index 0000000..4babcdc
--- /dev/null
+++ b/fw/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_cell_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/fw/esp32/main/component.mk b/fw/esp32/main/component.mk
new file mode 100644
index 0000000..61f8990
--- /dev/null
+++ b/fw/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.
+#