Support for ESP32 with ESP-IDF

PiperOrigin-RevId: 277408790
Change-Id: Ie1cc9ab7149db89b93508d21e7bfe7be40422433
This commit is contained in:
Frederic Rechtenstein 2019-10-29 18:32:07 -07:00 committed by TensorFlower Gardener
parent 1cc64d35f0
commit e9a3aa158a
12 changed files with 600 additions and 13 deletions

View File

@ -67,6 +67,65 @@ wave graphically. To view it, go to `Tools -> Serial Plotter`. You will see one
datapoint being logged for each inference cycle, expressed as a number between 0
and 255.
## Deploy to ESP32
The following instructions will help you build and deploy this sample
to [ESP32](https://www.espressif.com/en/products/hardware/esp32/overview)
devices using the [ESP IDF](https://github.com/espressif/esp-idf).
The sample has been tested on ESP-IDF version 4.0 with the following devices:
- [ESP32-DevKitC](http://esp-idf.readthedocs.io/en/latest/get-started/get-started-devkitc.html)
- [ESP-EYE](https://github.com/espressif/esp-who/blob/master/docs/en/get-started/ESP-EYE_Getting_Started_Guide.md)
### Install the ESP IDF
Follow the instructions of the
[ESP-IDF get started guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html)
to setup the toolchain and the ESP-IDF itself.
The next steps assume that the
[IDF environment variables are set](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html#step-4-set-up-the-environment-variables) :
* The `IDF_PATH` environment variable is set
* `idf.py` and Xtensa-esp32 tools (e.g. `xtensa-esp32-elf-gcc`) are in `$PATH`
### Generate the examples
The example project can be generated with the following command:
```
make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=esp generate_hello_world_esp_project
```
### Building the example
Go the the example project directory
```
cd tensorflow/lite/experimental/micro/tools/make/gen/esp_xtensa-esp32/prj/hello_world/esp-idf
```
Then build with `idf.py`
```
idf.py build
```
### Load and run the example
To flash (replace `/dev/ttyUSB0` with the device serial port):
```
idf.py --port /dev/ttyUSB0 flash
```
Monitor the serial output:
```
idf.py --port /dev/ttyUSB0 monitor
```
Use `Ctrl+]` to exit.
The previous two commands can be combined:
```
idf.py --port /dev/ttyUSB0 flash monitor
```
## Deploy to SparkFun Edge
The following instructions will help you build and deploy this sample on the

View File

@ -0,0 +1,23 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/lite/experimental/micro/examples/hello_world/main_functions.h"
extern "C" void app_main(void) {
setup();
while (true) {
loop();
}
}

View File

@ -184,6 +184,12 @@ KEIL_PROJECT_FILES := \
ARDUINO_PROJECT_FILES := \
library.properties
ESP_PROJECT_FILES := \
README_ESP.md \
CMakeLists.txt \
main/CMakeLists.txt \
components/tfmicro/CMakeLists.txt
ALL_PROJECT_TARGETS :=
ARDUINO_LIBRARY_TARGETS :=

View File

@ -112,33 +112,38 @@ define generate_arduino_project
$(PRJDIR)$(2)/arduino/examples/%.cpp: tensorflow/lite/experimental/micro/examples/%.cc
@mkdir -p $$(dir $$@)
@python tensorflow/lite/experimental/micro/tools/make/transform_arduino_source.py \
@python tensorflow/lite/experimental/micro/tools/make/transform_source.py \
--platform=arduino \
--is_example_source \
--source_path="$$<" \
--third_party_headers="$(4)" < $$< > $$@
$(PRJDIR)$(2)/arduino/examples/%.h: tensorflow/lite/experimental/micro/examples/%.h
@mkdir -p $$(dir $$@)
@python tensorflow/lite/experimental/micro/tools/make/transform_arduino_source.py \
@python tensorflow/lite/experimental/micro/tools/make/transform_source.py \
--platform=arduino \
--is_example_source \
--source_path="$$<" \
--third_party_headers="$(4)" < $$< > $$@
$(PRJDIR)$(2)/arduino/examples/%/main.ino: tensorflow/lite/experimental/micro/examples/%/main_functions.cc
@mkdir -p $$(dir $$@)
@python tensorflow/lite/experimental/micro/tools/make/transform_arduino_source.py \
@python tensorflow/lite/experimental/micro/tools/make/transform_source.py \
--platform=arduino \
--is_example_ino \
--source_path="$$<" \
--third_party_headers="$(4)" < $$< > $$@
$(PRJDIR)$(2)/arduino/src/%.cpp: %.cc
@mkdir -p $$(dir $$@)
@python tensorflow/lite/experimental/micro/tools/make/transform_arduino_source.py \
@python tensorflow/lite/experimental/micro/tools/make/transform_source.py \
--platform=arduino \
--third_party_headers="$(4)" < $$< > $$@
$(PRJDIR)$(2)/arduino/src/%.h: %.h third_party_downloads
@mkdir -p $$(dir $$@)
@python tensorflow/lite/experimental/micro/tools/make/transform_arduino_source.py \
@python tensorflow/lite/experimental/micro/tools/make/transform_source.py \
--platform=arduino \
--third_party_headers="$(4)" < $$< > $$@
$(PRJDIR)$(2)/arduino/LICENSE: LICENSE
@ -147,28 +152,33 @@ $(PRJDIR)$(2)/arduino/LICENSE: LICENSE
$(PRJDIR)$(2)/arduino/src/%: % third_party_downloads
@mkdir -p $$(dir $$@)
@python tensorflow/lite/experimental/micro/tools/make/transform_arduino_source.py \
@python tensorflow/lite/experimental/micro/tools/make/transform_source.py \
--platform=arduino \
--third_party_headers="$(4)" < $$< > $$@
$(PRJDIR)$(2)/arduino/src/third_party/%: tensorflow/lite/experimental/micro/tools/make/downloads/% third_party_downloads
@mkdir -p $$(dir $$@)
@python tensorflow/lite/experimental/micro/tools/make/transform_arduino_source.py \
@python tensorflow/lite/experimental/micro/tools/make/transform_source.py \
--platform=arduino \
--third_party_headers="$(4)" < $$< > $$@
$(PRJDIR)$(2)/arduino/src/third_party/%.cpp: tensorflow/lite/experimental/micro/tools/make/downloads/%.cc third_party_downloads
@mkdir -p $$(dir $$@)
@python tensorflow/lite/experimental/micro/tools/make/transform_arduino_source.py \
@python tensorflow/lite/experimental/micro/tools/make/transform_source.py \
--platform=arduino \
--third_party_headers="$(4)" < $$< > $$@
$(PRJDIR)$(2)/arduino/src/third_party/flatbuffers/include/flatbuffers/base.h: tensorflow/lite/experimental/micro/tools/make/downloads/flatbuffers/include/flatbuffers/base.h third_party_downloads
@mkdir -p $$(dir $$@)
@python tensorflow/lite/experimental/micro/tools/make/transform_arduino_source.py \
@python tensorflow/lite/experimental/micro/tools/make/transform_source.py \
--platform=arduino \
--third_party_headers="$(4)" < $$< | \
sed -E 's/utility\.h/utility/g' > $$@
$(PRJDIR)$(2)/arduino/src/third_party/kissfft/kiss_fft.h: tensorflow/lite/experimental/micro/tools/make/downloads/kissfft/kiss_fft.h third_party_downloads
@mkdir -p $$(dir $$@)
@python tensorflow/lite/experimental/micro/tools/make/transform_arduino_source.py \
@python tensorflow/lite/experimental/micro/tools/make/transform_source.py \
--platform=arduino \
--third_party_headers="$(4)" < $$< | \
sed -E 's@#include <string.h>@//#include <string.h> /* Patched by helper_functions.inc for Arduino compatibility */@g' > $$@
@ -217,6 +227,67 @@ ALL_PROJECT_TARGETS += $(if $(findstring _test,$(2)),,generate_$(2)_arduino_libr
endef
# Creates a set of rules to build a standalone ESP-IDF project for an
# executable, including all of the source and header files required in a
# separate folder.
# Arguments are:
# 1 - Project file template names.
# 2 - Name of executable.
# 3 - List of C/C++ source files needed to build the TF Micro component.
# 4 - List of C/C++ header files needed to build the TF Micro component.
# 5 - List of C/C++ source files needed to build this particular project.
# 6 - List of C/C++ header files needed to build this particular project.
# 7 - Linker flags required.
# 8 - C++ compilation flags needed.
# 9 - C compilation flags needed.
# 10 - List of includes.
define generate_esp_project
$(PRJDIR)$(2)/esp-idf/LICENSE: LICENSE
@mkdir -p $$(dir $$@)
@cp $$< $$@
$(PRJDIR)$(2)/esp-idf/main/%.cc: tensorflow/lite/experimental/micro/examples/$(2)/%.cc
@mkdir -p $$(dir $$@)
@python tensorflow/lite/experimental/micro/tools/make/transform_source.py \
--platform=esp \
--is_example_source \
--source_path="$$<" \
< $$< > $$@
$(PRJDIR)$(2)/esp-idf/main/%: tensorflow/lite/experimental/micro/examples/$(2)/%
@mkdir -p $$(dir $$@)
@cp $$< $$@
$(PRJDIR)$(2)/esp-idf/components/tfmicro/%: % third_party_downloads
@mkdir -p $$(dir $$@)
@cp $$< $$@
$(PRJDIR)$(2)/esp-idf/components/tfmicro/third_party/%: tensorflow/lite/experimental/micro/tools/make/downloads/% third_party_downloads
@mkdir -p $$(dir $$@)
@cp $$< $$@
$(PRJDIR)$(2)/esp-idf/%: tensorflow/lite/experimental/micro/tools/make/templates/esp/%.tpl
$(eval MAIN_SRCS_RELATIVE := $(patsubst tensorflow/lite/experimental/micro/examples/$(2)/%,%,$(5)))
@mkdir -p $$(dir $$@)
@sed -E 's#\%\{COMPONENT_SRCS\}\%#$(3)#g' $$< | \
sed -E 's#\%\{MAIN_SRCS\}\%#$(MAIN_SRCS_RELATIVE)#g' | \
sed -E 's#\%\{EXECUTABLE\}\%#$(2)#g' | \
sed -E 's#\%\{COMPONENT_INCLUDES\}\%#$(10)#g' | \
sed -E 's#\%\{LINKER_FLAGS\}\%#$(7)#g' | \
sed -E 's#\%\{CXX_FLAGS\}\%#$(8)#g' | \
sed -E 's#\%\{CC_FLAGS\}\%#$(9)#g' > $$@
generate_$(2)_esp_project: \
$(addprefix $(PRJDIR)$(2)/esp-idf/,\
$(patsubst tensorflow/%,components/tfmicro/tensorflow/%,\
$(patsubst third_party/%,components/tfmicro/third_party/%,\
$(patsubst tensorflow/lite/experimental/micro/examples/$(2)/%,main/%,$(3) $(4) $(5) $(6))))) \
$(addprefix $(PRJDIR)$(2)/esp-idf/,$(1))
ALL_PROJECT_TARGETS += generate_$(2)_esp_project
endef
# Specialized version of generate_project for TF Lite Micro test targets that
# automatically includes standard library files, so you just need to pass the
# test name and any extra source files required.
@ -231,6 +302,7 @@ $(call generate_project,make,$(MAKE_PROJECT_FILES),$(1),$(MICROLITE_CC_SRCS) $(T
$(call generate_project,mbed,$(MBED_PROJECT_FILES),$(1),$(MICROLITE_CC_SRCS) $(THIRD_PARTY_CC_SRCS) $(2),$(MICROLITE_CC_HDRS) $(THIRD_PARTY_CC_HDRS) $(MICROLITE_TEST_HDRS) $(3),$(MICROLITE_LIBS),$(CXXFLAGS),$(CCFLAGS))
$(call generate_project,keil,$(KEIL_PROJECT_FILES),$(1),$(MICROLITE_CC_SRCS) $(THIRD_PARTY_CC_SRCS) $(2),$(MICROLITE_CC_HDRS) $(THIRD_PARTY_CC_HDRS) $(MICROLITE_TEST_HDRS) $(3),$(MICROLITE_LIBS),$(CXXFLAGS),$(CCFLAGS))
$(call generate_arduino_project,$(ARDUINO_PROJECT_FILES),$(1),$(MICROLITE_CC_SRCS) $(THIRD_PARTY_CC_SRCS) $(2),$(MICROLITE_CC_HDRS) $(THIRD_PARTY_CC_HDRS) $(MICROLITE_TEST_HDRS) $(3),$(MICROLITE_LIBS),$(CXXFLAGS),$(CCFLAGS))
$(call generate_esp_project,$(ESP_PROJECT_FILES),$(1),$(MICROLITE_CC_SRCS) $(THIRD_PARTY_CC_SRCS),$(MICROLITE_CC_HDRS) $(THIRD_PARTY_CC_HDRS) $(MICROLITE_TEST_HDRS),$(2),$(3),$(MICROLITE_LIBS),$(CXXFLAGS),$(CCFLAGS),$(PROJECT_INCLUDES))
endef
# Handles the details of generating a binary target, including specializing

View File

@ -0,0 +1,5 @@
# Settings for Espressif ESP32
ifeq ($(TARGET), esp)
TARGET_ARCH := xtensa-esp32
endif

View File

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(%{EXECUTABLE}%)

View File

@ -0,0 +1,58 @@
# TensorFlow Lite Micro ESP-IDF Project
This folder has been autogenerated by TensorFlow, and contains source, header,
and project files needed to build a single TensorFlow Lite Micro target using
Espressif's [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/).
## Usage
### Install the ESP IDF
Follow the instructions of the
[ESP-IDF get started guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html)
to setup the toolchain and the ESP-IDF itself.
The next steps assume that the
[IDF environment variables are set](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html#step-4-set-up-the-environment-variables) :
* The `IDF_PATH` environment variable is set.
* `idf.py` and Xtensa-esp32 tools (e.g., `xtensa-esp32-elf-gcc`) are in `$PATH`.
## Build the example
To build this, run:
```
idf.py build
```
### Load and run the example
To flash (replace `/dev/ttyUSB0` with the device serial port):
```
idf.py --port /dev/ttyUSB0 flash
```
Monitor the serial output:
```
idf.py --port /dev/ttyUSB0 monitor
```
Use `Ctrl+]` to exit.
The previous two commands can be combined:
```
idf.py --port /dev/ttyUSB0 flash monitor
```
## Project Generation
See
[tensorflow/lite/experimental/micro](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/experimental/micro)
for details on how projects like this can be generated from the main source
tree.
## License
TensorFlow's code is covered by the Apache2 License included in the repository,
and third party dependencies are covered by their respective licenses, in the
third_party folder of this package.

View File

@ -0,0 +1,38 @@
# Copyright 2019 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This component was generated for the '%{EXECUTABLE}%' TF Micro example.
#
# Make sure that the IDF Path environment variable is defined
if(NOT DEFINED ENV{IDF_PATH})
message(FATAL_ERROR "The IDF_PATH environment variable must point to the location of the ESP-IDF.")
endif()
idf_component_register(
SRCS %{COMPONENT_SRCS}%
INCLUDE_DIRS %{COMPONENT_INCLUDES}%)
# Reduce the level of paranoia to be able to compile TF sources
target_compile_options(${COMPONENT_LIB} PRIVATE
-Wno-maybe-uninitialized
-Wno-missing-field-initializers
-Wno-pointer-sign
-Wno-type-limits)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} %{CC_FLAGS}%")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} %{CXX_FLAGS}%")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} %{LINKER_FLAGS}%")

View File

@ -0,0 +1,8 @@
#
# Main component of TF Micro project '%{EXECUTABLE}%'.
#
idf_component_register(
SRCS %{MAIN_SRCS}%
INCLUDE_DIRS "")

View File

@ -37,7 +37,8 @@ EOF
OUTPUT_REGULAR_FILE=${TEST_TMPDIR}/output_regular.cc
THIRD_PARTY_HEADERS="subdir/foo.h subdir_2/include/bar/fish.h subdir_3/something.h"
${TEST_SRCDIR}/tensorflow/lite/experimental/micro/tools/make/transform_arduino_source \
${TEST_SRCDIR}/tensorflow/lite/experimental/micro/tools/make/transform_source \
--platform=arduino \
--third_party_headers="${THIRD_PARTY_HEADERS}" \
< ${INPUT_REGULAR_FILE} \
> ${OUTPUT_REGULAR_FILE}
@ -89,7 +90,8 @@ EOF
OUTPUT_EXAMPLE_INO_FILE=${TEST_TMPDIR}/output_regular.cc
${TEST_SRCDIR}/tensorflow/lite/experimental/micro/tools/make/transform_arduino_source \
${TEST_SRCDIR}/tensorflow/lite/experimental/micro/tools/make/transform_source \
--platform=arduino \
--third_party_headers="${THIRD_PARTY_HEADERS}" \
--is_example_ino \
< ${INPUT_EXAMPLE_INO_FILE} \
@ -129,7 +131,8 @@ EOF
OUTPUT_EXAMPLE_SOURCE_FILE=${TEST_TMPDIR}/output_example_source.h
${TEST_SRCDIR}/tensorflow/lite/experimental/micro/tools/make/transform_arduino_source \
${TEST_SRCDIR}/tensorflow/lite/experimental/micro/tools/make/transform_source \
--platform=arduino \
--third_party_headers="${THIRD_PARTY_HEADERS}" \
--is_example_source \
--source_path="foo/input_example_source.h" \

View File

@ -0,0 +1,108 @@
#!/bin/bash
# Copyright 2019 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
#
# Bash unit tests for the TensorFlow Lite Micro project generator.
set -e
INPUT_EXAMPLE_FILE=${TEST_TMPDIR}/input_example.cc
cat << EOF > ${INPUT_EXAMPLE_FILE}
#include <stdio.h>
#include "baz.h"
#include "tensorflow/lite/experimental/micro/examples/something/foo/fish.h"
main() {
fprintf(stderr, "Hello World!\n");
return 0;
}
EOF
OUTPUT_EXAMPLE_FILE=${TEST_TMPDIR}/output_example.cc
${TEST_SRCDIR}/tensorflow/lite/experimental/micro/tools/make/transform_source \
--platform=esp \
--is_example_source \
--source_path="tensorflow/lite/experimental/micro/examples/something/input_example.cc" \
< ${INPUT_EXAMPLE_FILE} \
> ${OUTPUT_EXAMPLE_FILE}
if ! grep -q '#include <stdio.h>' ${OUTPUT_EXAMPLE_FILE}; then
echo "ERROR: No stdio.h include found in output '${OUTPUT_EXAMPLE_FILE}'"
exit 1
fi
if ! grep -q '#include "baz.h"' ${OUTPUT_EXAMPLE_FILE}; then
echo "ERROR: No baz.h include found in output '${OUTPUT_EXAMPLE_FILE}'"
exit 1
fi
if ! grep -q '#include "foo/fish.h"' ${OUTPUT_EXAMPLE_FILE}; then
echo "ERROR: No foo/fish.h include found in output '${OUTPUT_EXAMPLE_FILE}'"
exit 1
fi
#
# Example file in a sub directory.
#
mkdir -p "${TEST_TMPDIR}/subdir"
INPUT_EXAMPLE_SUBDIR_FILE=${TEST_TMPDIR}/subdir/input_example.cc
cat << EOF > ${INPUT_EXAMPLE_SUBDIR_FILE}
#include <stdio.h>
#include "baz.h"
#include "tensorflow/lite/experimental/micro/examples/something/subdir/input_example.h"
#include "tensorflow/lite/experimental/micro/examples/something/bleh.h"
#include "tensorflow/lite/experimental/micro/examples/something/foo/fish.h"
EOF
OUTPUT_EXAMPLE_SUBDIR_FILE=${TEST_TMPDIR}/output_example.cc
${TEST_SRCDIR}/tensorflow/lite/experimental/micro/tools/make/transform_source \
--platform=esp \
--is_example_source \
--source_path="tensorflow/lite/experimental/micro/examples/something/subdir/input_example.cc" \
< ${INPUT_EXAMPLE_SUBDIR_FILE} \
> ${OUTPUT_EXAMPLE_SUBDIR_FILE}
if ! grep -q '#include <stdio.h>' ${OUTPUT_EXAMPLE_SUBDIR_FILE}; then
echo "ERROR: No stdio.h include found in output '${OUTPUT_EXAMPLE_SUBDIR_FILE}'"
exit 1
fi
if ! grep -q '#include "baz.h"' ${OUTPUT_EXAMPLE_SUBDIR_FILE}; then
echo "ERROR: No baz.h include found in output '${OUTPUT_EXAMPLE_SUBDIR_FILE}'"
exit 1
fi
if ! grep -q '#include "input_example.h"' ${OUTPUT_EXAMPLE_SUBDIR_FILE}; then
echo "ERROR: No input_example.h include found in output '${OUTPUT_EXAMPLE_SUBDIR_FILE}'"
cat ${OUTPUT_EXAMPLE_SUBDIR_FILE}
exit 1
fi
if ! grep -q '#include "../bleh.h"' ${OUTPUT_EXAMPLE_SUBDIR_FILE}; then
echo "ERROR: No ../bleh.h include found in output '${OUTPUT_EXAMPLE_SUBDIR_FILE}'"
exit 1
fi
if ! grep -q '#include "../foo/fish.h"' ${OUTPUT_EXAMPLE_SUBDIR_FILE}; then
echo "ERROR: No ../foo/fish.h include found in output '${OUTPUT_EXAMPLE_SUBDIR_FILE}'"
exit 1
fi
echo
echo "SUCCESS: transform_esp_source test PASSED"

View File

@ -0,0 +1,204 @@
# Lint as: python2, python3
# Copyright 2019 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Resolves non-system C/C++ includes to their full paths.
Used to generate Arduino and ESP-IDF examples.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse
import os
import re
import sys
import six
EXAMPLE_DIR_PATH = 'tensorflow/lite/experimental/micro/examples/'
def replace_arduino_includes(line, supplied_headers_list):
"""Updates any includes to reference the new Arduino library paths."""
include_match = re.match(r'(.*#include.*")(.*)(")', line)
if include_match:
path = include_match.group(2)
for supplied_header in supplied_headers_list:
if six.ensure_str(supplied_header).endswith(path):
path = supplied_header
break
line = include_match.group(1) + six.ensure_str(path) + include_match.group(
3)
return line
def replace_arduino_main(line):
"""Updates any occurences of a bare main definition to the Arduino equivalent."""
main_match = re.match(r'(.*int )(main)(\(.*)', line)
if main_match:
line = main_match.group(1) + 'tflite_micro_main' + main_match.group(3)
return line
def check_ino_functions(input_text):
"""Ensures the required functions exist."""
# We're moving to an Arduino-friendly structure for all our examples, so they
# have to have a setup() and loop() function, just like their IDE expects.
if not re.search(r'void setup\(\) \{', input_text):
raise Exception(
'All examples must have a setup() function for Arduino compatiblity\n' +
input_text)
if not re.search(r'void loop\(\) \{', input_text):
raise Exception(
'All examples must have a loop() function for Arduino compatiblity')
return input_text
def add_example_ino_library_include(input_text):
"""Makes sure the example includes the header that loads the library."""
return re.sub(r'#include ', '#include <TensorFlowLite.h>\n\n#include ',
input_text, 1)
def replace_ardunio_example_includes(line, _):
"""Updates any includes for local example files."""
# Because the export process moves the example source and header files out of
# their default locations into the top-level 'examples' folder in the Arduino
# library, we have to update any include references to match.
dir_path = 'tensorflow/lite/experimental/micro/examples/'
include_match = re.match(
r'(.*#include.*")' + six.ensure_str(dir_path) + r'([^/]+)/(.*")', line)
if include_match:
flattened_name = re.sub(r'/', '_', include_match.group(3))
line = include_match.group(1) + flattened_name
return line
def replace_esp_example_includes(line, source_path):
"""Updates any includes for local example files."""
# Because the export process moves the example source and header files out of
# their default locations into the top-level 'main' folder in the ESP-IDF
# project, we have to update any include references to match.
include_match = re.match(r'.*#include.*"(' + EXAMPLE_DIR_PATH + r'.*)"', line)
if include_match:
# Compute the target path relative from the source's directory
target_path = include_match.group(1)
source_dirname = os.path.dirname(source_path)
rel_to_target = os.path.relpath(target_path, start=source_dirname)
line = '#include "%s"' % rel_to_target
return line
def transform_arduino_sources(input_lines, flags):
"""Transform sources for the Arduino platform.
Args:
input_lines: A sequence of lines from the input file to process.
flags: Flags indicating which transformation(s) to apply.
Returns:
The transformed output as a string.
"""
supplied_headers_list = six.ensure_str(flags.third_party_headers).split(' ')
output_lines = []
for line in input_lines:
line = replace_arduino_includes(line, supplied_headers_list)
if flags.is_example_ino or flags.is_example_source:
line = replace_ardunio_example_includes(line, flags.source_path)
else:
line = replace_arduino_main(line)
output_lines.append(line)
output_text = '\n'.join(output_lines)
if flags.is_example_ino:
output_text = check_ino_functions(output_text)
output_text = add_example_ino_library_include(output_text)
return output_text
def transform_esp_sources(input_lines, flags):
"""Transform sources for the ESP-IDF platform.
Args:
input_lines: A sequence of lines from the input file to process.
flags: Flags indicating which transformation(s) to apply.
Returns:
The transformed output as a string.
"""
output_lines = []
for line in input_lines:
if flags.is_example_source:
line = replace_esp_example_includes(line, flags.source_path)
output_lines.append(line)
output_text = '\n'.join(output_lines)
return output_text
def main(unused_args, flags):
"""Transforms the input source file to work when exported as example."""
input_file_lines = sys.stdin.read().split('\n')
output_text = ''
if flags.platform == 'arduino':
output_text = transform_arduino_sources(input_file_lines, flags)
elif flags.platform == 'esp':
output_text = transform_esp_sources(input_file_lines, flags)
sys.stdout.write(output_text)
def parse_args():
"""Converts the raw arguments into accessible flags."""
parser = argparse.ArgumentParser()
parser.add_argument(
'--platform',
choices=['arduino', 'esp'],
required=True,
help='Target platform.')
parser.add_argument(
'--third_party_headers',
type=str,
default='',
help='Space-separated list of headers to resolve.')
parser.add_argument(
'--is_example_ino',
dest='is_example_ino',
action='store_true',
help='Whether the destination is an example main ino.')
parser.add_argument(
'--is_example_source',
dest='is_example_source',
action='store_true',
help='Whether the destination is an example cpp or header file.')
parser.add_argument(
'--source_path',
type=str,
default='',
help='The relative path of the source code file.')
flags, unparsed = parser.parse_known_args()
main(unparsed, flags)
if __name__ == '__main__':
parse_args()