Setup sample_project structure
- build_overrides/pigweed.gni declares new modules.
e.g. $dir_pw_board_led
- Copy sample_project/workshop/{string,rpc} examples to applications/
Change-Id: Ia136853a382ab5f24d2dc269ee9bf22e2d855a7b
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/experimental/+/35000
Commit-Queue: Anthony DiGirolamo <tonymd@google.com>
Pigweed-Auto-Submit: Anthony DiGirolamo <tonymd@google.com>
Reviewed-by: Joe Ethier <jethier@google.com>
Reviewed-by: Rob Mohr <mohrr@google.com>
diff --git a/.clang-format b/.clang-format
new file mode 120000
index 0000000..1c7cede
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1 @@
+third_party/pigweed/.clang-format
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..a9a504a
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Force batch scripts to use CRLF.
+*.bat text eol=crlf
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..eb82667
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,48 @@
+# Build
+out/
+.presubmit/
+compile_commands.json
+
+# Environment
+.environment
+
+# Editors
+.idea/
+.project
+.cproject
+.vscode
+.clangd/
+*.swp
+*.swo
+
+# Python
+python*-env/
+.python*-env/
+venv/
+*.pyc
+*.egg/
+*.eggs/
+*.egg-info/
+.cache/
+.mypy_cache/
+__pycache__/
+
+# Mac
+.DS_Store
+
+# GDB
+.gdb_history
+
+# Git
+*.orig
+*.BACKUP.*
+*.BASE.*
+*.LOCAL.*
+*.REMOTE.*
+*_BACKUP_*.txt
+*_BASE_*.txt
+*_LOCAL_*.txt
+*_REMOTE_*.txt
+
+# Other
+logfile.txt
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..317cd91
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "third_party/pigweed"]
+ path = third_party/pigweed
+ url = https://pigweed.googlesource.com/pigweed/pigweed
+[submodule "third_party/nanopb"]
+ path = third_party/nanopb
+ url = https://pigweed.googlesource.com/third_party/github/nanopb/nanopb
diff --git a/.gn b/.gn
new file mode 100644
index 0000000..9e68813
--- /dev/null
+++ b/.gn
@@ -0,0 +1,15 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+buildconfig = "//BUILDCONFIG.gn"
diff --git a/BUILD.gn b/BUILD.gn
new file mode 100644
index 0000000..a246e17
--- /dev/null
+++ b/BUILD.gn
@@ -0,0 +1,121 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_arduino_build/arduino.gni")
+import("$dir_pw_build/python.gni")
+import("$dir_pw_tokenizer/database.gni")
+import("$dir_pw_unit_test/test.gni")
+
+# Lists all the targets build by default with e.g. `ninja -C out`.
+group("default") {
+ deps = [
+ ":applications",
+ ":arduino",
+ ":host",
+ ":python.lint",
+ ":python.tests",
+ ]
+}
+
+# Group all targets that run on host only, e.g. tests, utils.
+group("host") {
+ deps = [ ":host_tests(//targets/host:host_debug_tests)" ]
+}
+
+# Arduino specific targets.
+group("arduino") {
+ if (pw_arduino_build_CORE_PATH != "") {
+ deps = [ ":arduino_tests(//targets/arduino:arduino_debug_tests)" ]
+
+ if (pw_arduino_build_CORE_NAME == "teensy") {
+ deps += []
+ }
+ }
+}
+
+# stm32f429i_disc1 specific targets.
+group("stm32f429i_disc1") {
+ _default_toolchain = "//targets/stm32f429i-disc1:stm32f429i_disc1_debug"
+ _testing_toolchain = "${_default_toolchain}_tests"
+ deps = [
+ # ":app(${_default_toolchain})",
+ ":tests(${_testing_toolchain})",
+ ]
+}
+
+# Python packages are built using the host toolchain.
+pw_python_group("python") {
+ # This depends on the 'tools' target in //tools/BUILD.gn
+ python_deps = [
+ "tools",
+ "//third_party/pigweed/pw_env_setup:python",
+ "//third_party/pigweed/pw_env_setup:target_support_packages",
+ ]
+}
+
+group("applications") {
+ deps = []
+
+ # Teensy 3.0/4.0 applications steps.
+ if (pw_arduino_build_CORE_PATH != "") {
+ deps += [
+ ":applications_tests(//targets/arduino:arduino_debug_tests)",
+ "//applications/rpc:all(//targets/arduino:arduino_debug)",
+ "//applications/strings:all(//targets/arduino:arduino_debug)",
+ ]
+ }
+
+ # STMicroelectronics STM32F429I-DISC1 applications steps.
+ deps += [
+ ":applications_tests(//targets/stm32f429i-disc1:stm32f429i_disc1_debug_tests)",
+ "//applications/rpc:all(//targets/stm32f429i-disc1:stm32f429i_disc1_debug)",
+ "//applications/strings:all(//targets/stm32f429i-disc1:stm32f429i_disc1_debug)",
+ ]
+
+ # Host applications steps.
+ deps += [
+ ":applications_tests(//targets/host:host_debug_tests)",
+ "//applications/strings:all(//targets/host:host_debug)",
+ ]
+}
+
+# Group the different modules tests together.
+pw_test_group("tests") {
+ group_deps = [ "//applications/strings:tests" ]
+
+ if (pw_arduino_build_CORE_NAME == "teensy") {
+ group_deps += []
+ }
+}
+
+# Test groups for each platform that will build and optionally run the tests.
+foreach(test_group,
+ [
+ "arduino",
+ "host",
+ "applications",
+ ]) {
+ group("${test_group}_tests") {
+ deps = []
+ if (pw_unit_test_AUTOMATIC_RUNNER == "") {
+ # Without a test runner defined, build the tests but don't run them.
+ deps += [ ":tests" ]
+ } else {
+ # With a test runner, build and run tests.
+ deps += [ ":tests_run" ]
+ }
+ }
+}
diff --git a/BUILDCONFIG.gn b/BUILDCONFIG.gn
new file mode 100644
index 0000000..4452fbe
--- /dev/null
+++ b/BUILDCONFIG.gn
@@ -0,0 +1,23 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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 is imported into a scope so as not to pollute the global variable space.
+_pigweed_directory = {
+ import("//build_overrides/pigweed.gni")
+}
+
+# The default toolchain is not used in Pigweed builds, so it is set to a dummy
+# toolchain. The top-level BUILD.gn should stamp a group with all of the build
+# targets and their toolchains.
+set_default_toolchain("${_pigweed_directory.dir_pw_toolchain}/dummy")
diff --git a/PW_PLUGINS b/PW_PLUGINS
new file mode 100644
index 0000000..2d865e5
--- /dev/null
+++ b/PW_PLUGINS
@@ -0,0 +1,17 @@
+# The PW_PLUGINS file lists commands that should be included with the pw command
+# when it is invoked in this directory or its subdirectories. Commands in this
+# file override those registered in parent directories.
+#
+# Entries in this file have three columns:
+#
+# <name> <Python module> <function>
+#
+# The Python package containing that module must be installed in the Pigweed
+# virtual environment. The function must have no required arguments and should
+# return an int to use as the exit code.
+
+# Pigweed's presubmit check script
+presubmit pigweed_experimental_tools.presubmit_checks main
+heap-viewer pw_allocator.heap_viewer main
+rpc pw_hdlc.rpc_console main
+package pw_package.pigweed_packages main
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..327ef96
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+# Pigweed Experimental
+
+[TOC]
+
+This repository contains experimental pigweed modules.
+
+## Repository setup
+
+Clone this repo with `--recursive` to get all required submodules.
+
+```sh
+git clone --recursive https://pigweed.googlesource.com/pigweed/experimental
+```
+
+This will pull the [Pigweed source
+repository](https://pigweed.googlesource.com/pigweed/pigweed) into
+`third_party/pigweed`. If you already cloned but forgot to `--recursive` run
+`git submodule update --init` to pull all submodules.
+
diff --git a/activate.bat b/activate.bat
new file mode 100644
index 0000000..303ce4f
--- /dev/null
+++ b/activate.bat
@@ -0,0 +1,31 @@
+:<<"::WINDOWS_ONLY"
+@echo off
+:: Copyright 2021 The Pigweed Authors
+::
+:: 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
+::
+:: https://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.
+::WINDOWS_ONLY
+:; echo "ERROR: Attempting to run Windows .bat from a Unix/POSIX shell!"
+:; echo "Instead, run the following command."
+:; echo ""
+:; echo " source ./activate.sh"
+:; echo ""
+:<<"::WINDOWS_ONLY"
+
+:: First, activate the development environment.
+set PW_SKIP_BOOTSTRAP=1
+call "%~dp0\bootstrap.bat"
+set PW_SKIP_BOOTSTRAP=
+
+:: Add user-defined environment configuration here.
+
+::WINDOWS_ONLY
diff --git a/activate.sh b/activate.sh
new file mode 120000
index 0000000..eba5389
--- /dev/null
+++ b/activate.sh
@@ -0,0 +1 @@
+bootstrap.sh
\ No newline at end of file
diff --git a/applications/rpc/BUILD.gn b/applications/rpc/BUILD.gn
new file mode 100644
index 0000000..174cefc
--- /dev/null
+++ b/applications/rpc/BUILD.gn
@@ -0,0 +1,75 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_protobuf_compiler/proto.gni")
+import("$dir_pw_third_party/nanopb/nanopb.gni")
+import("$dir_pw_tokenizer/database.gni")
+
+config("default_config") {
+ include_dirs = [ "public" ]
+}
+
+group("all") {
+ deps = [ ":rpc" ]
+
+ # Build tokenizer_database for elf binaries only
+ if (host_os == "linux" || current_toolchain != "//targets/host:host_debug") {
+ deps += [ ":tokenizer_database" ]
+ }
+}
+
+pw_executable("rpc") {
+ sources = [ "main.cc" ]
+ deps = [
+ "$dir_pw_board_led",
+ "$dir_pw_spin_delay",
+ dir_pw_log,
+
+ # RPC related dependencies.
+ "$dir_pw_hdlc:pw_rpc",
+ "$dir_pw_hdlc:rpc_channel_output",
+ "$dir_pw_rpc:server",
+ "$dir_pw_stream:sys_io_stream",
+ dir_pw_hdlc,
+
+ # This is the service we are implementing!
+ ":remoticon_service_nanopb",
+
+ # This is an example service that just responds with that it was sent.
+ "$dir_pw_rpc/nanopb:echo_service",
+ ]
+}
+
+pw_tokenizer_database("tokenizer_database") {
+ database = "tokenizer_database.csv"
+ targets = [ ":rpc" ]
+}
+
+################################################################################
+# Service
+
+pw_proto_library("remoticon_proto") {
+ sources = [ "remoticon_proto/remoticon.proto" ]
+}
+
+pw_source_set("remoticon_service_nanopb") {
+ public_configs = [ ":default_config" ]
+ public_deps = [ ":remoticon_proto.nanopb_rpc" ]
+ public = [ "public/remoticon/remoticon_service_nanopb.h" ]
+ deps = [ ":remoticon_proto.nanopb_rpc" ]
+ sources = [ "remoticon_service_nanopb.cc" ]
+}
diff --git a/applications/rpc/README.md b/applications/rpc/README.md
new file mode 100644
index 0000000..752941a
--- /dev/null
+++ b/applications/rpc/README.md
@@ -0,0 +1,89 @@
+# RPC
+
+|||---|||
+
+*** aside
+#### [00: <br/> Setup](/workshop/README.md)
+
+`Intro + setup.`
+***
+
+*** aside
+#### [01: <br/> Blinky](/workshop/01-blinky/README.md)
+
+`Getting to blinky.`
+***
+
+*** aside
+#### [02: <br/> Testing](/workshop/02-string-functions/README.md)
+
+`Writing tests.`
+***
+
+*** promo
+#### [03: <br/> RPC](/workshop/03-rpc/README.md)
+
+`Calling RPCs.`
+***
+
+*** aside
+#### [04: <br/> KVS](/workshop/04-kvs/README.md)
+
+`Key Value Store.`
+***
+
+*** aside
+#### [05: <br/> FactoryTest](/workshop/05-factory-test/README.md)
+
+`Testing in the factory.`
+***
+
+|||---|||
+
+[TOC]
+
+## Build and Flash
+
+Instructions are the same as flashing [blinky](/workshop/01-blinky/README.md)
+but passing in a different `.elf`.
+
+1. Run the compile with `pw watch out` or `ninja -C out`.
+
+1. Flash `rpc.elf`.
+
+ **Teensy**
+
+ ```sh
+ arduino_unit_test_runner --config out/arduino_debug/gen/arduino_builder_config.json --upload-tool teensyloader --verbose --flash-only out/arduino_debug/obj/workshop/03-rpc/bin/rpc.elf
+ ```
+
+ **stm32f429i_disc1**
+
+ ```sh
+ openocd -s ${PW_PIGWEED_CIPD_INSTALL_DIR}/share/openocd/scripts -f ${PW_ROOT}/targets/stm32f429i-disc1/py/stm32f429i_disc1_utils/openocd_stm32f4xx.cfg -c "program out/arduino_debug/obj/workshop/03-rpc/bin/rpc.elf reset exit"
+ ```
+
+## View HDLC Encoded Log Output
+
+1. **Optional:** Create / update the log token database. This will be
+ automatically updated when compiling.
+
+ ```sh
+ python -m pw_tokenizer.database create --force --database workshop/03-rpc/tokenizer_database.csv out/arduino_debug/obj/workshop/03-rpc/bin/rpc.elf
+ ```
+
+1. Start the rpc_console that saves log output to a file.
+
+ ```sh
+ python -m pw_hdlc_lite.rpc_console -o logfile.txt -d /dev/ttyACM0 third_party/pigweed/pw_rpc/pw_rpc_protos/echo.proto workshop/03-rpc/remoticon_proto/remoticon.proto
+ ```
+
+ This will launch an interactive `ipython` console where you can send
+ RPCs. Try `rpcs.pw.rpc.EchoService.Echo(msg="hello!")`. IPython provides nice
+ tab completion for the RPC interfaces as well.
+
+1. Tail the log output.
+
+ ```sh
+ python -m pw_tokenizer.detokenize base64 workshop/03-rpc/tokenizer_database.csv -i logfile.txt --follow
+ ```
diff --git a/applications/rpc/main.cc b/applications/rpc/main.cc
new file mode 100644
index 0000000..c9ea5f0
--- /dev/null
+++ b/applications/rpc/main.cc
@@ -0,0 +1,192 @@
+// Copyright 2021 The Pigweed Authors
+//
+// 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
+//
+// https://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 <array>
+#include <span>
+#include <string_view>
+
+#include "pw_board_led/led.h"
+#include "pw_hdlc/encoder.h"
+#include "pw_hdlc/rpc_channel.h"
+#include "pw_hdlc/rpc_packets.h"
+#include "pw_log/log.h"
+#include "pw_rpc/echo_service_nanopb.h"
+#include "pw_rpc/server.h"
+#include "pw_spin_delay/delay.h"
+#include "pw_stream/sys_io_stream.h"
+#include "remoticon/remoticon_service_nanopb.h"
+
+// ------------------- superloop data -------------------
+// This is some "application" state that eventually exported by RPCs.
+
+unsigned superloop_iterations;
+
+// ------------------- pw_rpc subsystem setup -------------------
+// There are multiple ways to plumb pw_rpc in your product. In the future,
+// Pigweed may offer an optional pre-canned setup; but for now, you must
+// manually snap together the modular pieces.
+//
+// The RPC system in this code is layered as:
+//
+// UART --> pw_sys_io ------> hdlc -------> pw_rpc
+// (phy) (transport)
+//
+// HDLC converts the raw UART/serial byte stream into a packet stream. Then RPC
+// operates at the packet level.
+//
+// This is just one way to configure pw_rpc, which is designed to be flexible
+// and work over wh atever physical or logical transport you have available.
+
+constexpr size_t kMaxTransmissionUnit = 256; // bytes
+
+// Used to write HDLC data to pw::sys_io. This is an implementation of the
+// pw::stream::Stream interface.
+pw::stream::SysIoWriter sys_io_writer;
+
+// Set up the output channel for the pw_rpc server to use. This one happens to
+// implement the packet in / packet out with HDLC. pw_rpc can use any
+// ChannelOptput implementation, including custom ones for your product.
+pw::hdlc::RpcChannelOutputBuffer<kMaxTransmissionUnit> hdlc_channel_output(
+ sys_io_writer, pw::hdlc::kDefaultRpcAddress, "HDLC channel");
+
+// A pw::rpc::Server can have multiple channels (e.g. a UART and a BLE
+// connection). In this case, there is only one (HDLC over UART).
+pw::rpc::Channel channels[] = {
+ pw::rpc::Channel::Create<1>(&hdlc_channel_output)};
+
+// Declare the pw_rpc server with the HDLC channel.
+pw::rpc::Server server(channels);
+
+// Declare a buffer for decoding incoming HDLC frames.
+std::array<std::byte, kMaxTransmissionUnit> input_buffer;
+
+// Decoder object consumes bytes and return if a HDLC packet was completed.
+pw::hdlc::Decoder hdlc_decoder(input_buffer);
+
+// ------------------- pw_rpc service registration -------------------
+pw::rpc::EchoService echo_service;
+remoticon::SuperloopService superloop_service(superloop_iterations);
+
+// TODO FOR WORKSHOP: Declare your service object here!
+
+void RegisterServices() {
+ server.RegisterService(echo_service);
+ server.RegisterService(superloop_service);
+ // TODO FOR WORKSHOP: Register your service here!
+}
+
+// ------------------- pw_rpc -------------------
+
+constexpr unsigned kHdlcChannelForRpc = pw::hdlc::kDefaultRpcAddress;
+constexpr unsigned kHdlcChannelForLogs = 1;
+
+void ParseByteFromUartAndHandleRpcs() {
+ // Read a byte from the UART if one is available; if not, bail.
+ std::byte data;
+ if (!pw::sys_io::TryReadByte(&data).ok()) {
+ return;
+ }
+
+ // Byte received. Send the byte to the HDLC decoder; see if a packet finished.
+ auto result = hdlc_decoder.Process(data);
+
+ // Packet didn't parse correctly, so ignore it. In production, this should
+ // perhaps log or increment a metric (see pw_metric) to track bad packets.
+ if (!result.ok()) {
+ // POST-WORKSHOP EXERCISE: Add a tracking metric for bad packets, and
+ // expose the metric via the pw_metric RPC service. This will require
+ // making some metrics objects, incrementing them; then creating and
+ // registering a metric RPC service.
+ //
+ // See https://pigweed.dev/pw_metric/
+ // https://pigweed.dev/pw_metric/#exporting-metrics
+ return;
+ }
+
+ PW_LOG_INFO("Got complete HDLC packet");
+
+ // A frame was completed.
+ pw::hdlc::Frame& hdlc_frame = result.value();
+ if (hdlc_frame.address() != kHdlcChannelForRpc) {
+ // We ignore frames that are for unknown addresses, but you could put
+ // some code here if you wanted to stream custom data from PC --> device.
+ PW_LOG_WARN("Got packet with no destination; address: %d",
+ hdlc_frame.address());
+ return;
+ }
+
+ // Packet was validated and correct (CRC, etc); so send it to the RPC server.
+ // The RPC server may send response packets before returning from this call.
+ server.ProcessPacket(hdlc_frame.data(), hdlc_channel_output);
+}
+
+// TODO FOR WORKSHOP: Add an RPC to change the blink time.
+int state = 0;
+int counter = 0;
+// TODO FOR WORKSHOP: Change this value. 5M is good for Teensy 4.0; what value
+// is good for the Discovery?
+int counter_max = 5'000'000;
+
+void Blink() {
+ // Toggle the state if needed.
+ counter += 1;
+ // PW_LOG_INFO("Counter: %d", counter);
+ if (counter < counter_max) {
+ // Haven't hit a toggle event yet; bail.
+ return;
+ }
+ state = 1 - state;
+ counter = 0;
+
+ if (state == 0) {
+ PW_LOG_INFO("Blink High!");
+ pw::board_led::TurnOn();
+ } else {
+ PW_LOG_INFO("Blink Low!");
+ pw::board_led::TurnOff();
+ }
+}
+
+// TODO FOR WORKSHOP: Why doesn't this work, when of Blink() above does?
+void BlinkNoWorky() {
+ PW_LOG_INFO("Blink High!");
+ pw::board_led::TurnOn();
+ pw::spin_delay::WaitMillis(1000);
+
+ PW_LOG_INFO("Blink Low!");
+ pw::board_led::TurnOff();
+ pw::spin_delay::WaitMillis(1000);
+}
+
+int main() {
+ pw::board_led::Init();
+
+ PW_LOG_INFO("Registering pw_rpc services");
+ RegisterServices();
+
+ // Superloop!
+ while (true) {
+ // Toggle the LED if needed.
+ Blink();
+ // BlinkNoWorky(); // Pop quiz: This doesn't work. Why?
+
+ // Examine incoming serial byte; if a packet finished, send it to RPC.
+ ParseByteFromUartAndHandleRpcs();
+
+ // Increment the number of iterations.
+ superloop_iterations++;
+ }
+
+ return 0;
+}
diff --git a/applications/rpc/public/remoticon/remoticon_service_nanopb.h b/applications/rpc/public/remoticon/remoticon_service_nanopb.h
new file mode 100644
index 0000000..fa811b2
--- /dev/null
+++ b/applications/rpc/public/remoticon/remoticon_service_nanopb.h
@@ -0,0 +1,45 @@
+// Copyright 2021 The Pigweed Authors
+//
+// 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
+//
+// https://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.
+#pragma once
+
+#include <cstring>
+#include <span>
+
+#include "pw_status/status.h"
+#include "remoticon_proto/remoticon.rpc.pb.h"
+
+namespace remoticon {
+
+class SuperloopService final : public generated::Superloop<SuperloopService> {
+ public:
+ SuperloopService(unsigned& loop_iterations)
+ : loop_iterations_(loop_iterations) {}
+
+ // RPC method - this is exposed through the RPC server once the service is
+ // registered.
+ pw::Status GetStats(ServerContext&,
+ const remoticon_StatsRequest& request,
+ remoticon_StatsResponse& response);
+
+ // TODO FOR WORKSHOP: Add blink control.
+
+ private:
+ // Points to the super loop iteration count; this is updated externally.
+ //
+ // Note: In production storing an unsynchronized pointer is not a great
+ // pattern (no synchronization, no accessor/encapsulation).
+ unsigned& loop_iterations_;
+};
+
+} // namespace remoticon
diff --git a/applications/rpc/remoticon_proto/remoticon.proto b/applications/rpc/remoticon_proto/remoticon.proto
new file mode 100644
index 0000000..0abf925
--- /dev/null
+++ b/applications/rpc/remoticon_proto/remoticon.proto
@@ -0,0 +1,37 @@
+// Copyright 2021 The Pigweed Authors
+//
+// 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
+//
+// https://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.
+syntax = "proto3";
+
+package remoticon;
+
+// -------- A simple example RPC service ----
+message StatsRequest {}
+
+message StatsResponse {
+ uint32 loop_iterations = 1;
+}
+
+// For interacting with the system superloop.
+service Superloop {
+ rpc GetStats(StatsRequest) returns (StatsResponse) {}
+}
+
+// ------------------------------------------
+// TODO FOR WORKSHOP:
+//
+// Turn the LED on
+// Turn the LED off
+// Change blink time
+//
+// service LedService { ... }
diff --git a/applications/rpc/remoticon_service_nanopb.cc b/applications/rpc/remoticon_service_nanopb.cc
new file mode 100644
index 0000000..554844e
--- /dev/null
+++ b/applications/rpc/remoticon_service_nanopb.cc
@@ -0,0 +1,42 @@
+// Copyright 2021 The Pigweed Authors
+//
+// 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
+//
+// https://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 "remoticon/remoticon_service_nanopb.h"
+
+#include <cstring>
+#include <span>
+
+#include "pw_status/status.h"
+
+namespace remoticon {
+
+pw::Status SuperloopService::GetStats(
+ ServerContext&,
+ const remoticon_StatsRequest& /* request */,
+ remoticon_StatsResponse& response) {
+ // Send back the number of superloop iterations by setting the field in the
+ // response proto. In this case, the request proto is unused.
+ response.loop_iterations = loop_iterations_;
+
+ // pw_rpc's surrounding code handles serializing the nanopb StatsResponse
+ // struct and sending it out.
+ //
+ // Note: pw_rpc also supports raw methods where you are responsible for
+ // serializing and deserializing the request & response.
+ return pw::OkStatus();
+}
+
+// TODO FOR WORKSHOP: Implement methods for your service here!
+
+} // namespace remoticon
diff --git a/applications/rpc/tokenizer_database.csv b/applications/rpc/tokenizer_database.csv
new file mode 100644
index 0000000..3540a40
--- /dev/null
+++ b/applications/rpc/tokenizer_database.csv
@@ -0,0 +1,28 @@
+002007e1, ," "
+0f831860, ,"Failed to decode request payload from channel %u"
+0fbd23ba, ,"Failed to send response packet for channel %u"
+130e2c4b, ,"Received incomplete packet on interface %s"
+1ea870fb, ,"Blink Low!"
+2bc3cb0f, ," The current allocated heap memory is %u bytes."
+39b2c339, ,"Failed to decode packet on interface %s"
+3f774ea7, ,"Unable to handle packet of type %u"
+5a592684, ," malloc() is called %u times. (realloc()/calloc() counted as one time)"
+6271ad9a, ,"Got complete packet!"
+74627b3f, ,"Received CANCEL packet for method that is not pending"
+92a63809, ,"Received %lu-byte frame; frame must be at least 6 bytes"
+974389bc, ,"Got packet with no destination; address: %d"
+9dcacd5c, ," The cumulative allocated heap memory is %u bytes."
+aecb244f, ,"Frame size [%lu] exceeds the maximum buffer size [%lu]"
+af49fb70, ,"Blink High!"
+b0ebaaab, ,"Registering pw_rpc services"
+b12fd0ac, ,"Starting pw_rpc server"
+b3e37df6, ,"Frame check sequence verification failed"
+be290f47, ," free() is called %u times. (realloc() counted as one time)"
+c0c64a7a, ,"Got complete HDLC packet"
+c51612b3, ,"Failed to encode response packet to channel buffer"
+c5792a96, ," The current heap information: "
+d2d5d9aa, ,"Failed to encode response packet for channel %u"
+da03df49, ," The cumulative freed heap memory is %u bytes."
+e457fb7d, ," The total heap size is %u bytes."
+f716dbeb, ,"[ ]"
+fc0376f5, ,"[*]"
diff --git a/applications/strings/BUILD.gn b/applications/strings/BUILD.gn
new file mode 100644
index 0000000..7974be5
--- /dev/null
+++ b/applications/strings/BUILD.gn
@@ -0,0 +1,59 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_tokenizer/database.gni")
+import("$dir_pw_unit_test/test.gni")
+
+group("all") {
+ deps = [ ":string_demo" ]
+
+ # Build tokenizer_database for elf binaries only
+ if (host_os == "linux" || current_toolchain != "//targets/host:host_debug") {
+ deps += [ ":tokenizer_database" ]
+ }
+}
+
+pw_executable("string_demo") {
+ sources = [ "string_demo.cc" ]
+ deps = [
+ "$dir_pw_board_led",
+ "$dir_pw_hex_dump",
+ "$dir_pw_log",
+ "$dir_pw_spin_delay",
+ "$dir_pw_string",
+ ]
+}
+
+pw_source_set("system_status") {
+ public = [ "system_status.h" ]
+ public_deps = [ "$dir_pw_string" ]
+ sources = [ "system_status.cc" ]
+}
+
+pw_tokenizer_database("tokenizer_database") {
+ database = "tokenizer_database.csv"
+ targets = [ ":string_demo" ]
+}
+
+pw_test("system_status_test") {
+ deps = [ ":system_status" ]
+ sources = [ "system_status_test.cc" ]
+}
+
+pw_test_group("tests") {
+ tests = [ ":system_status_test" ]
+}
diff --git a/applications/strings/README.md b/applications/strings/README.md
new file mode 100644
index 0000000..5978bc4
--- /dev/null
+++ b/applications/strings/README.md
@@ -0,0 +1,151 @@
+# Unit Tests & Strings
+
+|||---|||
+
+*** aside
+#### [00: <br/> Setup](/workshop/README.md)
+
+`Intro + setup.`
+***
+
+*** aside
+#### [01: <br/> Blinky](/workshop/01-blinky/README.md)
+
+`Getting to blinky.`
+***
+
+*** promo
+#### [02: <br/> Testing](/workshop/02-string-functions/README.md)
+
+`Writing tests.`
+***
+
+*** aside
+#### [03: <br/> RPC](/workshop/03-rpc/README.md)
+
+`Calling RPCs.`
+***
+
+*** aside
+#### [04: <br/> KVS](/workshop/04-kvs/README.md)
+
+`Key Value Store.`
+***
+
+*** aside
+#### [05: <br/> FactoryTest](/workshop/05-factory-test/README.md)
+
+`Testing in the factory.`
+***
+
+|||---|||
+
+[TOC]
+
+## Build and Flash
+
+Instructions are the same as flashing [blinky](/workshop/01-blinky/README.md)
+but passing in a different `.elf`.
+
+1. Run the compile with `pw watch out` or `ninja -C out`.
+
+1. Flash the test `.elf`
+
+ ```sh
+ arduino_unit_test_runner --config out/arduino_debug/gen/arduino_builder_config.json --upload-tool teensyloader --verbose --flash-only out/arduino_debug/obj/workshop/02-string-functions/bin/string_demo.elf
+ ```
+
+1. Tail the output with `miniterm`, (use `Ctrl-]` to quit).
+
+ ```sh
+ python -m serial.tools.miniterm --raw - 115200
+ ```
+
+ You should see something like:
+
+ ```text
+ $ python -m serial.tools.miniterm --raw - 115200
+ --- Available ports:
+ --- 1: /dev/ttyACM0 'USB Serial'
+ --- Enter port index or full name: 1
+ --- Miniterm on /dev/ttyACM0 115200,8,N,1 ---
+ --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
+ INF Teensy Time: RTC has set the system time.
+ INF Offs. 0 1 2 3 4 5 6 7 8 9 a b c d e f Text
+ INF 0000: 53 75 70 65 72 20 53 69 6d 70 6c 65 20 54 69 6d Super Simple Tim
+ INF 0010: 65 20 4c 6f 67 67 69 6e 67 21 00 e Logging!.
+ INF 2020-11-04 14:35:35
+ INF 2020-11-04 14:35:36
+ INF 2020-11-04 14:35:37
+ INF 2020-11-04 14:35:38
+ INF 2020-11-04 14:35:39
+
+ --- exit ---
+ ```
+
+ All tests are set to use plain text logging. This is specified by the
+ `pw_log_BACKEND` variable in the `target_toolchain.gni` files. For example
+ the `arduino_debug_tests` toolchain in
+ [`//targets/arduino/target_toolchains.gni`](/targets/arduino/target_toolchains.gni)
+ defines: `pw_log_BACKEND = "$dir_pw_log_basic"`
+
+## Exercise
+
+**Goal:** Write a test for the
+[`GetStatusString()`](/workshop/02-string-functions/system_status.cc#9)
+function.
+
+It can be added to
+[/workshop/02-string-functions/system_status_test.cc](/workshop/02-string-functions/system_status_test.cc).
+
+*** promo
+- Refer to the [gTest string comparison
+ documentation](https://github.com/google/googletest/blob/master/googletest/docs/primer.md#string-comparison)
+ for how to check the contents of strings.
+- Note that [gMock
+ matchers](https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#more-string-assertions)
+ are not supported in Pigweed yet.
+***
+
+## Runing Tests
+
+### Run a Single Test Manually
+
+After compiling use `arduino_unit_test_runner` to flash and check test output.
+
+ **Teensy**
+
+ ```sh
+ arduino_unit_test_runner --config out/arduino_debug/gen/arduino_builder_config.json --upload-tool teensyloader --verbose out/arduino_debug/obj/workshop/02-string-functions/test/system_status_test.elf
+ ```
+
+ **stm32f429i_disc1**
+
+ ```sh
+ stm32f429i_disc1_unit_test_runner --verbose out/stm32f429i_disc1_debug/obj/workshop/02-string-functions/test/system_status_test.elf
+ ```
+
+
+### Using the Test Server.
+
+1. Make sure the `pw_arduino_use_test_server=true` build arg is set.
+
+1. Start the unit test server for your board.
+
+ **Teensy**
+
+ ```sh
+ arduino_test_server --verbose --config-file ./out/arduino_debug/gen/arduino_builder_config.json
+ ```
+
+ **stm32f429i_disc1**
+
+ ```sh
+ stm32f429i_disc1_test_server --verbose
+ ```
+
+1. In a separate terminal start pw watch.
+
+ ```sh
+ pw watch out
+ ```
diff --git a/applications/strings/string_demo.cc b/applications/strings/string_demo.cc
new file mode 100644
index 0000000..27be3f9
--- /dev/null
+++ b/applications/strings/string_demo.cc
@@ -0,0 +1,61 @@
+// Copyright 2021 The Pigweed Authors
+//
+// 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
+//
+// https://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 <array>
+#include <span>
+
+#include "pw_board_led/led.h"
+#include "pw_hex_dump/hex_dump.h"
+#include "pw_log/log.h"
+#include "pw_spin_delay/delay.h"
+#include "pw_string/format.h"
+#include "pw_string/string_builder.h"
+
+int main() {
+ pw::board_led::Init();
+
+ const char my_data[] = "Super Simple Status Logging";
+ std::array<char, 80> hex_dump_buffer;
+ pw::dump::FormattedHexDumper hex_dumper(hex_dump_buffer);
+ hex_dumper.BeginDump(std::as_bytes(std::span(my_data)));
+ while (hex_dumper.DumpLine().ok()) {
+ PW_LOG_INFO("%s", hex_dump_buffer.data());
+ }
+
+ unsigned seconds = 0;
+ unsigned update_count = 0;
+
+ char buffer[64];
+ while (true) {
+ pw::string::Format(buffer,
+ "[%d-%02d-%02d %02d:%02d:%02d] Message number: %u",
+ 2020,
+ 11,
+ 8,
+ 14,
+ 15,
+ seconds,
+ update_count);
+
+ PW_LOG_INFO("%s", buffer);
+
+ pw::board_led::TurnOn();
+ pw::spin_delay::WaitMillis(1000);
+
+ seconds = (seconds + 1) % 60;
+ update_count = (update_count + 1) % 65535;
+ }
+
+ return 0;
+}
diff --git a/applications/strings/system_status.cc b/applications/strings/system_status.cc
new file mode 100644
index 0000000..0f2795f
--- /dev/null
+++ b/applications/strings/system_status.cc
@@ -0,0 +1,16 @@
+#include "system_status.h"
+
+#include <cinttypes>
+
+#include "pw_string/string_builder.h"
+
+namespace system_status {
+
+const char* GetStatusString(unsigned led_state) {
+ pw::StringBuffer<64> sb;
+ sb << "[SystemStatus] ";
+ sb.Format("LED: %02u", led_state);
+ return sb.data();
+}
+
+} // namespace system_status
diff --git a/applications/strings/system_status.h b/applications/strings/system_status.h
new file mode 100644
index 0000000..ab1ff11
--- /dev/null
+++ b/applications/strings/system_status.h
@@ -0,0 +1,20 @@
+// Copyright 2021 The Pigweed Authors
+//
+// 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
+//
+// https://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.
+#pragma once
+
+namespace system_status {
+
+const char* GetStatusString(unsigned led_state);
+
+} // namespace system_status
diff --git a/applications/strings/system_status_test.cc b/applications/strings/system_status_test.cc
new file mode 100644
index 0000000..9e2382d
--- /dev/null
+++ b/applications/strings/system_status_test.cc
@@ -0,0 +1,27 @@
+// Copyright 2021 The Pigweed Authors
+//
+// 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
+//
+// https://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 "system_status.h"
+
+#include "gtest/gtest.h"
+#include "pw_string/string_builder.h"
+
+namespace system_status {
+
+TEST(GetStatusString, LEDState) {
+ // TODO: Add test here!
+ EXPECT_TRUE(true);
+}
+
+} // namespace system_status
diff --git a/applications/strings/tokenizer_database.csv b/applications/strings/tokenizer_database.csv
new file mode 100644
index 0000000..64799f0
--- /dev/null
+++ b/applications/strings/tokenizer_database.csv
@@ -0,0 +1,13 @@
+002007e1, ," "
+1ea870fb, ,"Blink Low!"
+2bc3cb0f, ," The current allocated heap memory is %u bytes."
+38c60010, ,"%s"
+5a592684, ," malloc() is called %u times. (realloc()/calloc() counted as one time)"
+9dcacd5c, ," The cumulative allocated heap memory is %u bytes."
+af49fb70, ,"Blink High!"
+be290f47, ," free() is called %u times. (realloc() counted as one time)"
+c5792a96, ," The current heap information: "
+da03df49, ," The cumulative freed heap memory is %u bytes."
+e457fb7d, ," The total heap size is %u bytes."
+f716dbeb, ,"[ ]"
+fc0376f5, ,"[*]"
diff --git a/banner.txt b/banner.txt
new file mode 100644
index 0000000..7a6b653
--- /dev/null
+++ b/banner.txt
@@ -0,0 +1,2 @@
+[1m[35m ╱╱╱ [1m[35m█▀█ █░█░█  █▀▀ ▀▄▀ █▀█ █▀▀ █▀█ █ █▀▄▀█ █▀▀ █▄░█ ▀█▀ ▄▀█ █░░
+[1m[35m ╱╱╱ [1m[35m█▀▀ ▀▄▀▄▀  ██▄ █░█ █▀▀ ██▄ █▀▄ █ █░▀░█ ██▄ █░▀█ ░█░ █▀█ █▄▄
diff --git a/bootstrap.bat b/bootstrap.bat
new file mode 100644
index 0000000..89d9e1b
--- /dev/null
+++ b/bootstrap.bat
@@ -0,0 +1,50 @@
+:<<"::WINDOWS_ONLY"
+@echo off
+:: Copyright 2021 The Pigweed Authors
+::
+:: 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
+::
+:: https://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.
+::WINDOWS_ONLY
+:; echo "ERROR: Attempting to run Windows .bat from a Unix/POSIX shell!"
+:; echo "Instead, run the following command."
+:; echo ""
+:; echo " source ./activate.sh"
+:; echo ""
+:<<"::WINDOWS_ONLY"
+
+:: The bootstrap.bat must be run initially to install all required programs.
+:: After that, use activate.bat to enter the environment in a shell.
+
+:: First, activate the Pigweed development environment.
+set "_pw_bootstrap_script=%~dp0.\third_party\pigweed\bootstrap.bat"
+set "PW_PROJECT_ROOT=%~dp0."
+set "PIGWEED_EXPERIMENTAL_ROOT=%PW_PROJECT_ROOT%"
+
+:: Set your project's banner and color.
+set "PW_BRANDING_BANNER=%PW_PROJECT_ROOT%\banner.txt"
+set "PW_BRANDING_BANNER_COLOR=cyan"
+
+if not exist "%_pw_bootstrap_script%" (
+ echo Error: "%_pw_bootstrap_script%" not found.
+ echo Did you forget to initialize the git submodules?
+ echo To setup the git submodules run:
+ echo git submodule init
+ echo git submodule update
+ goto finish
+)
+
+call "%_pw_bootstrap_script%"
+
+:: Add user-defined initial setup here.
+
+:finish
+::WINDOWS_ONLY
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..6cd0205
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,141 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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 script must be sourced, not executed.
+#
+# Create a new environment:
+#
+# source bootstrap.sh
+#
+# Activate an existing environment:
+#
+# source activate.sh
+
+_bootstrap_abspath () {
+ python -c "import os.path; print(os.path.abspath('$@'))"
+}
+
+# Users are not expected to set PW_CHECKOUT_ROOT, it's only used because it
+# seems to be impossible to reliably determine the path to a sourced file in
+# dash when sourced from a dash script instead of a dash interactive prompt.
+# To reinforce that users should not be using PW_CHECKOUT_ROOT, it is cleared
+# here after it is used, and other pw tools will complain if they see that
+# variable set.
+# TODO(mohrr) find out a way to do this without PW_CHECKOUT_ROOT.
+if test -n "$PW_CHECKOUT_ROOT"; then
+ _BOOTSTRAP_PATH="$(_bootstrap_abspath "$PW_CHECKOUT_ROOT/bootstrap.sh")"
+ unset PW_CHECKOUT_ROOT
+# Shell: bash.
+elif test -n "$BASH"; then
+ _BOOTSTRAP_PATH="$(_bootstrap_abspath "$BASH_SOURCE")"
+# Shell: zsh.
+elif test -n "$ZSH_NAME"; then
+ _BOOTSTRAP_PATH="$(_bootstrap_abspath "${(%):-%N}")"
+# Shell: dash.
+elif test ${0##*/} = dash; then
+ _BOOTSTRAP_PATH="$(_bootstrap_abspath \
+ "$(lsof -p $$ -Fn0 | tail -1 | sed 's#^[^/]*##;')")"
+# If everything else fails, try $0. It could work.
+else
+ _BOOTSTRAP_PATH="$(_bootstrap_abspath "$0")"
+fi
+
+# Check if this file is being executed or sourced.
+_pw_sourced=0
+if [ -n "$SWARMING_BOT_ID" ]; then
+ # If set we're running on swarming and don't need this check.
+ _pw_sourced=1
+elif [ -n "$ZSH_EVAL_CONTEXT" ]; then
+ case $ZSH_EVAL_CONTEXT in *:file) _pw_sourced=1;; esac
+elif [ -n "$KSH_VERSION" ]; then
+ [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != \
+ "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] \
+ && _pw_sourced=1
+elif [ -n "$BASH_VERSION" ]; then
+ (return 0 2>/dev/null) && _pw_sourced=1
+else # All other shells: examine $0 for known shell binary filenames
+ # Detects `sh` and `dash`; add additional shell filenames as needed.
+ case ${0##*/} in sh|dash) _pw_sourced=1;; esac
+fi
+
+# Downstream projects need to set something other than PW_ROOT here, like
+# YOUR_PROJECT_ROOT. Please also set PW_PROJECT_ROOT and PW_ROOT before
+# invoking pw_bootstrap or pw_activate.
+######### BEGIN PROJECT-SPECIFIC CODE #########
+PIGWEED_EXPERIMENTAL_ROOT="$(_bootstrap_abspath "$(dirname "$_BOOTSTRAP_PATH")")"
+export PIGWEED_EXPERIMENTAL_ROOT
+PW_PROJECT_ROOT="$PIGWEED_EXPERIMENTAL_ROOT"
+PW_ROOT="$PIGWEED_EXPERIMENTAL_ROOT/third_party/pigweed"
+
+# Set your project's banner and color.
+export PW_BRANDING_BANNER="$PIGWEED_EXPERIMENTAL_ROOT/banner.txt"
+export PW_BRANDING_BANNER_COLOR=magenta
+
+PIGWEED_EXPERIMENTAL_banner() {
+ cat "$PW_BRANDING_BANNER"
+ echo
+}
+
+PW_BANNER_FUNC="PIGWEED_EXPERIMENTAL_banner"
+########## END PROJECT-SPECIFIC CODE ##########
+export PW_BANNER_FUNC
+export PW_PROJECT_ROOT
+export PW_ROOT
+
+_util_sh="$PW_ROOT/pw_env_setup/util.sh"
+
+# Double-check that the Pigweed submodule has been checked out.
+if [ ! -e "$_util_sh" ]; then
+ echo "Error: $_util_sh not found."
+ echo "Did you forget to initialize the git submodules?"
+ echo "To setup the git submodules run:"
+ echo " git submodule update --init"
+ return
+fi
+
+. "$_util_sh"
+
+pw_deactivate
+pw_eval_sourced "$_pw_sourced"
+pw_check_root "$PW_ROOT"
+_PW_ACTUAL_ENVIRONMENT_ROOT="$(pw_get_env_root)"
+export _PW_ACTUAL_ENVIRONMENT_ROOT
+SETUP_SH="$_PW_ACTUAL_ENVIRONMENT_ROOT/activate.sh"
+
+# Run full bootstrap when invoked as bootstrap, or env file is missing/empty.
+if [ "$(basename "$_BOOTSTRAP_PATH")" = "bootstrap.sh" ] || \
+ [ ! -f "$SETUP_SH" ] || \
+ [ ! -s "$SETUP_SH" ]; then
+# This is where pw_bootstrap is called. Most small projects will include
+# --use-pigweed-defaults.
+######### BEGIN PROJECT-SPECIFIC CODE #########
+ pw_bootstrap --shell-file "$SETUP_SH" --install-dir "$_PW_ACTUAL_ENVIRONMENT_ROOT" --use-pigweed-defaults --virtualenv-gn-target "$PW_PROJECT_ROOT#:python.install" --virtualenv-gn-out-dir "$PW_PROJECT_ROOT/out"
+########## END PROJECT-SPECIFIC CODE ##########
+ pw_finalize bootstrap "$SETUP_SH"
+else
+ pw_activate
+ pw_finalize activate "$SETUP_SH"
+fi
+
+# This is where any additional checks about the environment should go.
+######### BEGIN PROJECT-SPECIFIC CODE #########
+########## END PROJECT-SPECIFIC CODE ##########
+
+unset _pw_sourced
+unset _BOOTSTRAP_PATH
+unset SETUP_SH
+unset _bootstrap_abspath
+unset _util_sh
+
+pw_cleanup
diff --git a/build_overrides/pigweed.gni b/build_overrides/pigweed.gni
new file mode 100644
index 0000000..d627ec5
--- /dev/null
+++ b/build_overrides/pigweed.gni
@@ -0,0 +1,39 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+declare_args() {
+ # Location of the Pigweed repository.
+ dir_pigweed = "//third_party/pigweed/"
+}
+
+# Upstream Pigweed modules.
+import("$dir_pigweed/modules.gni")
+
+# Experimental modules.
+declare_args() {
+ dir_pw_board_led = get_path_info("//modules/pw_board_led", "abspath")
+ dir_pw_board_led_host =
+ get_path_info("//modules/pw_board_led_host", "abspath")
+ dir_pw_board_led_stm32f429i_disc1 =
+ get_path_info("//modules/pw_board_led_stm32f429i_disc1", "abspath")
+ dir_pw_board_led_arduino =
+ get_path_info("//modules/pw_board_led_arduino", "abspath")
+ dir_pw_spin_delay = get_path_info("//modules/pw_spin_delay", "abspath")
+ dir_pw_spin_delay_arduino =
+ get_path_info("//modules/pw_spin_delay_arduino", "abspath")
+ dir_pw_spin_delay_host =
+ get_path_info("//modules/pw_spin_delay_host", "abspath")
+ dir_pw_spin_delay_stm32f429i_disc1 =
+ get_path_info("//modules/pw_spin_delay_stm32f429i_disc1", "abspath")
+}
diff --git a/modules/pw_board_led/BUILD.gn b/modules/pw_board_led/BUILD.gn
new file mode 100644
index 0000000..bf09f23
--- /dev/null
+++ b/modules/pw_board_led/BUILD.gn
@@ -0,0 +1,32 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/facade.gni")
+
+declare_args() {
+ # The backend (driver) to use for the board LED.
+ pw_board_led_BACKEND = ""
+}
+
+config("public_includes") {
+ include_dirs = [ "public" ]
+}
+
+pw_facade("pw_board_led") {
+ backend = pw_board_led_BACKEND
+ public_configs = [ ":public_includes" ]
+ public = [ "public/pw_board_led/led.h" ]
+}
diff --git a/modules/pw_board_led/public/pw_board_led/led.h b/modules/pw_board_led/public/pw_board_led/led.h
new file mode 100644
index 0000000..972a480
--- /dev/null
+++ b/modules/pw_board_led/public/pw_board_led/led.h
@@ -0,0 +1,25 @@
+// Copyright 2021 The Pigweed Authors
+//
+// 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
+//
+// https://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.
+#pragma once
+
+namespace pw::board_led {
+
+void Init();
+void TurnOn();
+void TurnOff();
+void Toggle();
+
+// TODO(amontanez): Maybe we should add a GetState()? 🤔
+
+} // namespace pw::board_led
diff --git a/modules/pw_board_led_arduino/BUILD.gn b/modules/pw_board_led_arduino/BUILD.gn
new file mode 100644
index 0000000..c88215b
--- /dev/null
+++ b/modules/pw_board_led_arduino/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_arduino_build/arduino.gni")
+import("$dir_pw_build/target_types.gni")
+
+pw_source_set("pw_board_led_arduino") {
+ deps = [
+ "$dir_pw_board_led:pw_board_led.facade",
+ "$dir_pw_third_party/arduino:arduino_core_sources",
+ ]
+ sources = [ "led.cc" ]
+ remove_configs = [ "$dir_pw_build:strict_warnings" ]
+}
diff --git a/modules/pw_board_led_arduino/led.cc b/modules/pw_board_led_arduino/led.cc
new file mode 100644
index 0000000..3f9a85f
--- /dev/null
+++ b/modules/pw_board_led_arduino/led.cc
@@ -0,0 +1,53 @@
+// Copyright 2021 The Pigweed Authors
+//
+// 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
+//
+// https://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 "pw_board_led/led.h"
+
+#include <Arduino.h>
+
+#include <cinttypes>
+
+namespace pw::board_led {
+namespace {
+
+constexpr int kLedPin = 13;
+bool led_on = false;
+
+} // namespace
+
+void Init() {
+ pinMode(kLedPin, OUTPUT);
+ TurnOff();
+}
+
+void TurnOff() {
+ digitalWrite(kLedPin, LOW);
+ led_on = false;
+}
+
+void TurnOn() {
+ digitalWrite(kLedPin, HIGH);
+ led_on = true;
+}
+
+void Toggle() {
+ // Check if the LED is on. If so, turn it off.
+ if (led_on) {
+ TurnOff();
+ } else {
+ TurnOn();
+ }
+}
+
+} // namespace pw::board_led
diff --git a/modules/pw_board_led_host/BUILD.gn b/modules/pw_board_led_host/BUILD.gn
new file mode 100644
index 0000000..0266972
--- /dev/null
+++ b/modules/pw_board_led_host/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+pw_source_set("pw_board_led_host") {
+ deps = [
+ "$dir_pw_board_led:pw_board_led.facade",
+ dir_pw_log,
+ ]
+ sources = [ "led.cc" ]
+}
diff --git a/modules/pw_board_led_host/led.cc b/modules/pw_board_led_host/led.cc
new file mode 100644
index 0000000..1aa8cf3
--- /dev/null
+++ b/modules/pw_board_led_host/led.cc
@@ -0,0 +1,49 @@
+// Copyright 2021 The Pigweed Authors
+//
+// 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
+//
+// https://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 "pw_board_led/led.h"
+
+#include <cinttypes>
+
+#include "pw_log/log.h"
+
+namespace pw::board_led {
+namespace {
+
+bool led_on = false;
+
+} // namespace
+
+void Init() { TurnOff(); }
+
+void TurnOff() {
+ PW_LOG_INFO("[ ]");
+ led_on = false;
+}
+
+void TurnOn() {
+ PW_LOG_INFO("[*]");
+ led_on = true;
+}
+
+void Toggle() {
+ // Check if the LED is on. If so, turn it off.
+ if (led_on) {
+ TurnOff();
+ } else {
+ TurnOn();
+ }
+}
+
+} // namespace pw::board_led
diff --git a/modules/pw_board_led_stm32f429i_disc1/BUILD.gn b/modules/pw_board_led_stm32f429i_disc1/BUILD.gn
new file mode 100644
index 0000000..65f6342
--- /dev/null
+++ b/modules/pw_board_led_stm32f429i_disc1/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+pw_source_set("pw_board_led_stm32f429i_disc1") {
+ deps = [
+ "$dir_pw_board_led:pw_board_led.facade",
+ dir_pw_preprocessor,
+ ]
+ sources = [ "led.cc" ]
+}
diff --git a/modules/pw_board_led_stm32f429i_disc1/led.cc b/modules/pw_board_led_stm32f429i_disc1/led.cc
new file mode 100644
index 0000000..8f5247a
--- /dev/null
+++ b/modules/pw_board_led_stm32f429i_disc1/led.cc
@@ -0,0 +1,108 @@
+// Copyright 2021 The Pigweed Authors
+//
+// 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
+//
+// https://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 "pw_board_led/led.h"
+
+#include <cinttypes>
+
+#include "pw_preprocessor/compiler.h"
+
+namespace pw::board_led {
+namespace {
+
+// Base address for everything peripheral-related on the STM32F4xx.
+constexpr uint32_t kPeripheralBaseAddr = 0x40000000u;
+// Base address for everything AHB1-related on the STM32F4xx.
+constexpr uint32_t kAhb1PeripheralBase = kPeripheralBaseAddr + 0x00020000U;
+// Base address for everything APB2-related on the STM32F4xx.
+constexpr uint32_t kApb2PeripheralBase = kPeripheralBaseAddr + 0x00010000U;
+
+// Reset/clock configuration block (RCC).
+// `reserved` fields are unimplemented features, and are present to ensure
+// proper alignment of registers that are in use.
+PW_PACKED(struct) RccBlock {
+ uint32_t reserved1[12];
+ uint32_t ahb1_config;
+ uint32_t reserved2[4];
+ uint32_t apb2_config;
+};
+
+// GPIO register block definition.
+PW_PACKED(struct) GpioBlock {
+ uint32_t modes;
+ uint32_t out_type;
+ uint32_t out_speed;
+ uint32_t pull_up_down;
+ uint32_t input_data;
+ uint32_t output_data;
+ uint32_t gpio_bit_set;
+ uint32_t port_config_lock;
+ uint32_t alt_low;
+ uint32_t alt_high;
+};
+
+// Constants related to GPIO mode register masks.
+constexpr uint32_t kGpioPortModeMask = 0x3u;
+constexpr uint32_t kGpio13PortModePos = 26;
+constexpr uint32_t kGpioPortModeOutput = 1;
+
+// Constants related to GPIO output mode register masks.
+constexpr uint32_t kGpioOutputModeMask = 0x1u;
+constexpr uint32_t kGpio13OutputModePos = 13;
+constexpr uint32_t kGpioOutputModePushPull = 0;
+
+constexpr uint32_t kGpio13BitSetHigh = 0x1u << 13;
+constexpr uint32_t kGpio13BitSetLow = kGpio13BitSetHigh << 16;
+
+// Mask for ahb1_config (AHB1ENR) to enable the "G" GPIO pins.
+constexpr uint32_t kGpioGEnable = 0x1u << 6;
+
+// Declare a reference to the memory mapped RCC block.
+volatile RccBlock& platform_rcc =
+ *reinterpret_cast<volatile RccBlock*>(kAhb1PeripheralBase + 0x3800U);
+
+// Declare a reference to the 'G' GPIO memory mapped block.
+volatile GpioBlock& gpio_g =
+ *reinterpret_cast<volatile GpioBlock*>(kAhb1PeripheralBase + 0x1800U);
+
+} // namespace
+
+void Init() {
+ // Enable 'G' GIPO clocks.
+ platform_rcc.ahb1_config |= kGpioGEnable;
+
+ // Enable Pin 13 in output mode.
+ gpio_g.modes = (gpio_g.modes & ~(kGpioPortModeMask << kGpio13PortModePos)) |
+ (kGpioPortModeOutput << kGpio13PortModePos);
+
+ // Enable Pin 13 in output mode "push pull"
+ gpio_g.out_type =
+ (gpio_g.out_type & ~(kGpioOutputModeMask << kGpio13OutputModePos)) |
+ (kGpioOutputModePushPull << kGpio13OutputModePos);
+}
+
+void TurnOff() { gpio_g.gpio_bit_set = kGpio13BitSetLow; }
+
+void TurnOn() { gpio_g.gpio_bit_set = kGpio13BitSetHigh; }
+
+void Toggle() {
+ // Check if the LED is on. If so, turn it off.
+ if (gpio_g.output_data & kGpio13BitSetHigh) {
+ TurnOff();
+ } else {
+ TurnOn();
+ }
+}
+
+} // namespace pw::board_led
diff --git a/modules/pw_spin_delay/BUILD.gn b/modules/pw_spin_delay/BUILD.gn
new file mode 100644
index 0000000..41b9275
--- /dev/null
+++ b/modules/pw_spin_delay/BUILD.gn
@@ -0,0 +1,32 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/facade.gni")
+
+declare_args() {
+ # The backend (driver) to use for the board LED.
+ pw_spin_delay_BACKEND = ""
+}
+
+config("public_includes") {
+ include_dirs = [ "public" ]
+}
+
+pw_facade("pw_spin_delay") {
+ backend = pw_spin_delay_BACKEND
+ public_configs = [ ":public_includes" ]
+ public = [ "public/pw_spin_delay/delay.h" ]
+}
diff --git a/modules/pw_spin_delay/public/pw_spin_delay/delay.h b/modules/pw_spin_delay/public/pw_spin_delay/delay.h
new file mode 100644
index 0000000..3e83651
--- /dev/null
+++ b/modules/pw_spin_delay/public/pw_spin_delay/delay.h
@@ -0,0 +1,25 @@
+// Copyright 2021 The Pigweed Authors
+//
+// 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
+//
+// https://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.
+#pragma once
+
+#include <cinttypes>
+#include <cstddef>
+
+namespace pw::spin_delay {
+
+void WaitMillis(size_t delay_ms);
+uint32_t Millis();
+uint32_t Micros();
+
+} // namespace pw::spin_delay
diff --git a/modules/pw_spin_delay_arduino/BUILD.gn b/modules/pw_spin_delay_arduino/BUILD.gn
new file mode 100644
index 0000000..d7410cc
--- /dev/null
+++ b/modules/pw_spin_delay_arduino/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_arduino_build/arduino.gni")
+import("$dir_pw_build/target_types.gni")
+
+pw_source_set("pw_spin_delay_arduino") {
+ deps = [
+ "$dir_pw_spin_delay:pw_spin_delay.facade",
+ "$dir_pw_third_party/arduino:arduino_core_sources",
+ ]
+ sources = [ "delay.cc" ]
+ remove_configs = [ "$dir_pw_build:strict_warnings" ]
+}
diff --git a/modules/pw_spin_delay_arduino/delay.cc b/modules/pw_spin_delay_arduino/delay.cc
new file mode 100644
index 0000000..ccb0d77
--- /dev/null
+++ b/modules/pw_spin_delay_arduino/delay.cc
@@ -0,0 +1,27 @@
+// Copyright 2021 The Pigweed Authors
+//
+// 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
+//
+// https://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 "pw_spin_delay/delay.h"
+
+#include <Arduino.h>
+
+#include <cstddef>
+
+namespace pw::spin_delay {
+
+void WaitMillis(size_t delay_ms) { delay(delay_ms); }
+uint32_t Millis() { return millis(); }
+uint32_t Micros() { return micros(); }
+
+} // namespace pw::spin_delay
diff --git a/modules/pw_spin_delay_host/BUILD.gn b/modules/pw_spin_delay_host/BUILD.gn
new file mode 100644
index 0000000..fea87c4
--- /dev/null
+++ b/modules/pw_spin_delay_host/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+pw_source_set("pw_spin_delay_host") {
+ deps = [ "$dir_pw_spin_delay:pw_spin_delay.facade" ]
+ sources = [ "delay.cc" ]
+}
diff --git a/modules/pw_spin_delay_host/delay.cc b/modules/pw_spin_delay_host/delay.cc
new file mode 100644
index 0000000..0e0c1c4
--- /dev/null
+++ b/modules/pw_spin_delay_host/delay.cc
@@ -0,0 +1,61 @@
+// Copyright 2021 The Pigweed Authors
+//
+// 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
+//
+// https://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 "pw_spin_delay/delay.h"
+
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
+
+namespace {
+std::chrono::system_clock::time_point kProgramStart =
+ std::chrono::system_clock::now();
+}
+
+namespace pw::spin_delay {
+
+void WaitMillis(size_t delay_ms) {
+ std::chrono::system_clock::time_point start =
+ std::chrono::system_clock::now();
+ std::chrono::system_clock::time_point now;
+ size_t difference;
+ do {
+ now = std::chrono::system_clock::now();
+ difference =
+ std::chrono::duration_cast<std::chrono::milliseconds>(now - start)
+ .count();
+ } while (difference < delay_ms);
+}
+
+uint32_t Millis() {
+ std::chrono::system_clock::time_point now;
+ uint32_t difference;
+ now = std::chrono::system_clock::now();
+ difference =
+ std::chrono::duration_cast<std::chrono::milliseconds>(now - kProgramStart)
+ .count();
+ return difference;
+}
+
+uint32_t Micros() {
+ std::chrono::system_clock::time_point now;
+ uint32_t difference;
+ now = std::chrono::system_clock::now();
+ difference =
+ std::chrono::duration_cast<std::chrono::microseconds>(now - kProgramStart)
+ .count();
+ return difference;
+}
+
+} // namespace pw::spin_delay
diff --git a/modules/pw_spin_delay_stm32f429i_disc1/BUILD.gn b/modules/pw_spin_delay_stm32f429i_disc1/BUILD.gn
new file mode 100644
index 0000000..6708b33
--- /dev/null
+++ b/modules/pw_spin_delay_stm32f429i_disc1/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+pw_source_set("pw_spin_delay_stm32f429i_disc1") {
+ deps = [
+ "$dir_pw_spin_delay:pw_spin_delay.facade",
+ dir_pw_preprocessor,
+ ]
+ sources = [ "delay.cc" ]
+}
diff --git a/modules/pw_spin_delay_stm32f429i_disc1/delay.cc b/modules/pw_spin_delay_stm32f429i_disc1/delay.cc
new file mode 100644
index 0000000..780575b
--- /dev/null
+++ b/modules/pw_spin_delay_stm32f429i_disc1/delay.cc
@@ -0,0 +1,57 @@
+// Copyright 2021 The Pigweed Authors
+//
+// 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
+//
+// https://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 "pw_spin_delay/delay.h"
+
+#include <cstddef>
+#include <cstdint>
+
+namespace pw::spin_delay {
+
+// !!!WARNING!!!: This delay is not truly accurate! It's mostly just a rough
+// estimate! Also, it only works in a baremetal context with no interrupts
+// getting in the way or threads getting CPU time.
+//
+// TODO(amontanez): Replace this implementation with a loop checking a
+// pw_chrono clock.
+void WaitMillis(size_t delay_ms) {
+ // Default core clock. This is technically not a constant, but since Pigweed
+ // doesn't change the system clock a constant will suffice.
+ constexpr uint32_t kSystemCoreClock = 16000000;
+ constexpr uint32_t kCyclesPerMs = kSystemCoreClock / 1000;
+
+ // This is not totally accurate, but is close enough.
+ for (size_t i = 0; i < delay_ms; i++) {
+ // Do a 4 instruction loop enough times to be running for a millisecond.
+ // This is set up with assembly rather than a regular loop to make the
+ // instruction count predictable (no compiler variation).
+ uint32_t cycles = kCyclesPerMs;
+ asm volatile(
+ " mov r0, %[cycles] \n"
+ " mov r1, #0 \n"
+ "loop: \n"
+ " cmp r1, r0 \n"
+ " itt lt \n"
+ " addlt r1, r1, #4 \n"
+ " blt loop \n"
+ // clang-format off
+ : /*output=*/
+ : /*input=*/[cycles]"r"(cycles)
+ : /*clobbers=*/"r0", "r1"
+ // clang-format on
+ );
+ }
+}
+
+} // namespace pw::spin_delay
diff --git a/navbar.md b/navbar.md
new file mode 100644
index 0000000..0324365
--- /dev/null
+++ b/navbar.md
@@ -0,0 +1,3 @@
+# Pigweed Experimental
+
+* [Home](/README.md)
diff --git a/targets/arduino/BUILD.gn b/targets/arduino/BUILD.gn
new file mode 100644
index 0000000..200c021
--- /dev/null
+++ b/targets/arduino/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_toolchain/generate_toolchain.gni")
+import("target_toolchains.gni")
+
+generate_toolchains("toolchains") {
+ toolchains = toolchains_list
+}
diff --git a/targets/arduino/target_toolchains.gni b/targets/arduino/target_toolchains.gni
new file mode 100644
index 0000000..d556802
--- /dev/null
+++ b/targets/arduino/target_toolchains.gni
@@ -0,0 +1,63 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("//targets/common_backends.gni")
+import("$dir_pigweed/targets/arduino/target_toolchains.gni")
+import("$dir_pw_protobuf_compiler/proto.gni")
+import("$dir_pw_third_party/nanopb/nanopb.gni")
+
+target_toolchain_arduino = {
+ _excluded_members = [
+ "defaults",
+ "name",
+ ]
+
+ arduino_debug = {
+ name = "arduino_debug"
+ _toolchain_base = pw_target_toolchain_arduino.debug
+ forward_variables_from(_toolchain_base, "*", _excluded_members)
+ defaults = {
+ forward_variables_from(_toolchain_base.defaults, "*")
+ forward_variables_from(toolchain_overrides, "*")
+
+ # Configure backend for pw_board_led
+ pw_board_led_BACKEND = "$dir_pw_board_led_arduino"
+
+ # Configure backend for pw_spin_delay
+ pw_spin_delay_BACKEND = "$dir_pw_spin_delay_arduino"
+ }
+ }
+
+ # Toolchain for tests only.
+ arduino_debug_tests = {
+ name = "arduino_debug_tests"
+ _toolchain_base = pw_target_toolchain_arduino.debug
+ forward_variables_from(_toolchain_base, "*", _excluded_members)
+ defaults = {
+ forward_variables_from(_toolchain_base.defaults, "*")
+ forward_variables_from(toolchain_overrides, "*")
+
+ # Force tests to use basic log backend to avoid generating and loading its
+ # own tokenized database.
+ pw_log_BACKEND = "$dir_pw_log_basic"
+ }
+ }
+}
+
+toolchains_list = [
+ target_toolchain_arduino.arduino_debug,
+ target_toolchain_arduino.arduino_debug_tests,
+]
diff --git a/targets/common_backends.gni b/targets/common_backends.gni
new file mode 100644
index 0000000..eebec8b
--- /dev/null
+++ b/targets/common_backends.gni
@@ -0,0 +1,32 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+# Inherit from a Pigweed upstream toolchain and override backends as needed.
+toolchain_overrides = {
+ # Configure backend for assert facade.
+ pw_assert_BACKEND = "$dir_pw_assert_basic"
+
+ # Configure the pw_log facade for Base64 tokenized logging.
+ pw_log_BACKEND = "$dir_pw_log_tokenized:log_backend"
+ pw_tokenizer_GLOBAL_HANDLER_WITH_PAYLOAD_BACKEND =
+ "$dir_pw_log_tokenized:base64_over_hdlc"
+
+ # Alternately, configure pw_log for plain text logging
+ # pw_log_BACKEND = "$dir_pw_log_basic"
+
+ # Path to the nanopb installation. Defaults to included git module.
+ dir_pw_third_party_nanopb = "//third_party/nanopb"
+}
diff --git a/targets/host/BUILD.gn b/targets/host/BUILD.gn
new file mode 100644
index 0000000..200c021
--- /dev/null
+++ b/targets/host/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_toolchain/generate_toolchain.gni")
+import("target_toolchains.gni")
+
+generate_toolchains("toolchains") {
+ toolchains = toolchains_list
+}
diff --git a/targets/host/target_toolchains.gni b/targets/host/target_toolchains.gni
new file mode 100644
index 0000000..72e8cf6
--- /dev/null
+++ b/targets/host/target_toolchains.gni
@@ -0,0 +1,80 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("//targets/common_backends.gni")
+import("$dir_pigweed/targets/host/target_toolchains.gni")
+import("$dir_pw_protobuf_compiler/proto.gni")
+import("$dir_pw_third_party/nanopb/nanopb.gni")
+
+target_toolchain_host = {
+ _excluded_members = [
+ "defaults",
+ "name",
+ ]
+
+ _excluded_defaults = [
+ "pw_trace_BACKEND",
+ "pw_trace_tokenizer_time",
+ ]
+
+ clang_debug = {
+ name = "host_debug"
+ if (host_os == "win") {
+ _toolchain_base = pw_target_toolchain_host.gcc_debug
+ } else {
+ _toolchain_base = pw_target_toolchain_host.clang_debug
+ }
+ forward_variables_from(_toolchain_base, "*", _excluded_members)
+ defaults = {
+ forward_variables_from(_toolchain_base.defaults, "*", _excluded_defaults)
+ forward_variables_from(toolchain_overrides, "*")
+
+ # Force hosts to use basic log backend to avoid generating and loading its
+ # own tokenized database.
+ pw_log_BACKEND = "$dir_pw_log_basic"
+
+ # Configure backend for pw_sys_io facade.
+ pw_sys_io_BACKEND = "$dir_pw_sys_io_stdio"
+
+ pw_board_led_BACKEND = "$dir_pw_board_led_host"
+ pw_spin_delay_BACKEND = "$dir_pw_spin_delay_host"
+ }
+ }
+
+ # Toolchain for tests only.
+ clang_debug_tests = {
+ name = "host_debug_tests"
+ if (host_os == "win") {
+ _toolchain_base = pw_target_toolchain_host.gcc_debug
+ } else {
+ _toolchain_base = pw_target_toolchain_host.clang_debug
+ }
+ forward_variables_from(_toolchain_base, "*", _excluded_members)
+ defaults = {
+ forward_variables_from(_toolchain_base.defaults, "*", _excluded_defaults)
+ forward_variables_from(toolchain_overrides, "*")
+
+ # Force tests to use basic log backend to avoid generating and loading its
+ # own tokenized database.
+ pw_log_BACKEND = "$dir_pw_log_basic"
+ }
+ }
+}
+
+toolchains_list = [
+ target_toolchain_host.clang_debug,
+ target_toolchain_host.clang_debug_tests,
+]
diff --git a/targets/stm32f429i-disc1/BUILD.gn b/targets/stm32f429i-disc1/BUILD.gn
new file mode 100644
index 0000000..200c021
--- /dev/null
+++ b/targets/stm32f429i-disc1/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_toolchain/generate_toolchain.gni")
+import("target_toolchains.gni")
+
+generate_toolchains("toolchains") {
+ toolchains = toolchains_list
+}
diff --git a/targets/stm32f429i-disc1/target_toolchains.gni b/targets/stm32f429i-disc1/target_toolchains.gni
new file mode 100644
index 0000000..1746ce1
--- /dev/null
+++ b/targets/stm32f429i-disc1/target_toolchains.gni
@@ -0,0 +1,64 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("//targets/common_backends.gni")
+import("$dir_pigweed/targets/stm32f429i-disc1/target_toolchains.gni")
+import("$dir_pw_protobuf_compiler/proto.gni")
+import("$dir_pw_third_party/nanopb/nanopb.gni")
+
+target_toolchain_stm32f429i_disc1 = {
+ _excluded_members = [
+ "defaults",
+ "name",
+ ]
+ _excluded_defaults = [
+ "pw_cpu_exception_ENTRY_BACKEND",
+ "pw_cpu_exception_HANDLER_BACKEND",
+ "pw_cpu_exception_SUPPORT_BACKEND",
+ ]
+
+ debug = {
+ name = "stm32f429i_disc1_debug"
+ _toolchain_base = pw_target_toolchain_stm32f429i_disc1.debug
+ forward_variables_from(_toolchain_base, "*", _excluded_members)
+ defaults = {
+ forward_variables_from(_toolchain_base.defaults, "*", _excluded_defaults)
+ forward_variables_from(toolchain_overrides, "*")
+ pw_board_led_BACKEND = "$dir_pw_board_led_stm32f429i_disc1"
+ pw_spin_delay_BACKEND = "$dir_pw_spin_delay_stm32f429i_disc1"
+ }
+ }
+
+ # Toolchain for tests only.
+ debug_tests = {
+ name = "stm32f429i_disc1_debug_tests"
+ _toolchain_base = pw_target_toolchain_stm32f429i_disc1.debug
+ forward_variables_from(_toolchain_base, "*", _excluded_members)
+ defaults = {
+ forward_variables_from(_toolchain_base.defaults, "*", _excluded_defaults)
+ forward_variables_from(toolchain_overrides, "*")
+
+ # Force tests to use basic log backend to avoid generating and loading its
+ # own tokenized database.
+ pw_log_BACKEND = "$dir_pw_log_basic"
+ }
+ }
+}
+
+toolchains_list = [
+ target_toolchain_stm32f429i_disc1.debug,
+ target_toolchain_stm32f429i_disc1.debug_tests,
+]
diff --git a/third_party/nanopb b/third_party/nanopb
new file mode 160000
index 0000000..049485f
--- /dev/null
+++ b/third_party/nanopb
@@ -0,0 +1 @@
+Subproject commit 049485ff557178f646d573eca3bd647f543b760b
diff --git a/third_party/pigweed b/third_party/pigweed
new file mode 160000
index 0000000..b3ea980
--- /dev/null
+++ b/third_party/pigweed
@@ -0,0 +1 @@
+Subproject commit b3ea980f10c763ea8d018c017886691afa992993
diff --git a/tools/BUILD.gn b/tools/BUILD.gn
new file mode 100644
index 0000000..b93a53a
--- /dev/null
+++ b/tools/BUILD.gn
@@ -0,0 +1,30 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/python.gni")
+
+pw_python_package("tools") {
+ setup = [ "setup.py" ]
+ sources = [
+ "pigweed_experimental_tools/__init__.py",
+ "pigweed_experimental_tools/presubmit_checks.py",
+ ]
+ python_deps = [
+ "$dir_pw_cli/py",
+ "$dir_pw_presubmit/py",
+ ]
+ pylintrc = "$dir_pigweed/.pylintrc"
+}
diff --git a/tools/pigweed_experimental_tools/__init__.py b/tools/pigweed_experimental_tools/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/pigweed_experimental_tools/__init__.py
diff --git a/tools/pigweed_experimental_tools/presubmit_checks.py b/tools/pigweed_experimental_tools/presubmit_checks.py
new file mode 100755
index 0000000..c5635f1
--- /dev/null
+++ b/tools/pigweed_experimental_tools/presubmit_checks.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python3
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+"""Example presubmit check script."""
+
+import argparse
+import logging
+import os
+from pathlib import Path
+import re
+import sys
+
+try:
+ import pw_cli.log
+except ImportError:
+ print('ERROR: Activate the environment before running presubmits!',
+ file=sys.stderr)
+ sys.exit(2)
+
+import pw_presubmit
+from pw_presubmit import build, cli, environment, format_code, git_repo
+from pw_presubmit import python_checks, filter_paths, PresubmitContext
+from pw_presubmit.install_hook import install_hook
+
+_LOG = logging.getLogger(__name__)
+
+# Set up variables for key project paths.
+try:
+ PROJECT_ROOT = Path(os.environ['PIGWEED_EXPERIMENTAL_ROOT'])
+except KeyError:
+ print(
+ 'ERROR: The presubmit checks must be run in the sample project\'s root'
+ ' directory',
+ file=sys.stderr)
+ sys.exit(2)
+
+PIGWEED_ROOT = PROJECT_ROOT / 'third_party' / 'pigweed'
+REPOS = (
+ PROJECT_ROOT,
+ PIGWEED_ROOT,
+ PROJECT_ROOT / 'third_party' / 'nanopb',
+)
+
+
+#
+# Initialization
+#
+def init_cipd(ctx: PresubmitContext):
+ """Initialize CIPD for project dependencies."""
+ environment.init_cipd(PIGWEED_ROOT, ctx.output_dir)
+
+
+def init_virtualenv(ctx: PresubmitContext):
+ """Initialize a virtual environment to run presubmits."""
+ environment.init_virtualenv(
+ PIGWEED_ROOT,
+ ctx.output_dir,
+ gn_targets=(
+ f'{ctx.root}#:python.install',
+ f'{ctx.root}/third_party/pigweed#:python.install',
+ f'{ctx.root}/third_party/pigweed#:target_support_packages.install',
+ ))
+
+
+# Rerun the build if files with these extensions change.
+_BUILD_EXTENSIONS = frozenset(
+ ['.rst', '.gn', '.gni', *format_code.C_FORMAT.extensions])
+
+
+#
+# Presubmit checks
+#
+def default_build(ctx: PresubmitContext):
+ """Creates a default build."""
+ build.gn_gen(PROJECT_ROOT, ctx.output_dir)
+ build.ninja(ctx.output_dir)
+
+
+def check_for_git_changes(_: PresubmitContext):
+ """Checks that repositories have all changes commited."""
+ checked_repos = (PIGWEED_ROOT, *REPOS)
+ changes = [r for r in checked_repos if git_repo.has_uncommitted_changes(r)]
+ for repo in changes:
+ _LOG.error('There are uncommitted changes in the %s repo!', repo.name)
+ if changes:
+ _LOG.warning(
+ 'Commit or stash pending changes before running the presubmit.')
+ raise pw_presubmit.PresubmitFailure
+
+
+# Avoid running some checks on certain paths.
+PATH_EXCLUSIONS = (
+ re.compile(r'^external/'),
+ re.compile(r'^third_party/'),
+ re.compile(r'^vendor/'),
+)
+
+
+# Use the upstream pragma_once check, but apply a different set of path
+# filters with @filter_paths.
+@filter_paths(endswith='.h', exclude=PATH_EXCLUSIONS)
+def pragma_once(ctx: PresubmitContext):
+ """Presubmit check that ensures all header files contain '#pragma once'."""
+ pw_presubmit.pragma_once(ctx)
+
+
+#
+# Presubmit check programs
+#
+QUICK = (
+ # List some presubmit checks to run
+ default_build,
+ # Use the upstream formatting checks, with custom path filters applied.
+ format_code.presubmit_checks(exclude=PATH_EXCLUSIONS),
+)
+FULL = (
+ # Initialize an environment for running presubmit checks.
+ init_cipd,
+ init_virtualenv,
+ pragma_once,
+ QUICK, # Add all checks from the 'quick' program
+ # Use the upstream Python checks, with custom path filters applied.
+ python_checks.all_checks(exclude=PATH_EXCLUSIONS),
+)
+PROGRAMS = pw_presubmit.Programs(quick=QUICK, full=FULL)
+
+
+def run(install: bool, **presubmit_args) -> int:
+ """Process the --install argument then invoke pw_presubmit."""
+
+ # Install the presubmit Git pre-push hook, if requested.
+ if install:
+ install_hook(__file__, 'pre-push', ['--base', 'HEAD~'],
+ git_repo.root())
+ return 0
+
+ return cli.run(root=PROJECT_ROOT, **presubmit_args)
+
+
+def main() -> int:
+ """Run the presubmit checks for this repository."""
+ parser = argparse.ArgumentParser(description=__doc__)
+ cli.add_arguments(parser, PROGRAMS, 'quick')
+
+ # Define an option for installing a Git pre-push hook for this script.
+ parser.add_argument(
+ '--install',
+ action='store_true',
+ help='Install the presubmit as a Git pre-push hook and exit.')
+
+ return run(**vars(parser.parse_args()))
+
+
+if __name__ == '__main__':
+ pw_cli.log.install(logging.INFO)
+ sys.exit(main())
diff --git a/tools/pigweed_experimental_tools/py.typed b/tools/pigweed_experimental_tools/py.typed
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/pigweed_experimental_tools/py.typed
diff --git a/tools/setup.py b/tools/setup.py
new file mode 100644
index 0000000..7f4c6ff
--- /dev/null
+++ b/tools/setup.py
@@ -0,0 +1,34 @@
+# Copyright 2021 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+"""Utilities for Sample Project development."""
+
+import setuptools # type: ignore
+
+setuptools.setup(
+ name='pigweed_experimental_tools',
+ version='0.0.1',
+ author='Pigweed Authors',
+ author_email='pigweed-developers@googlegroups.com',
+ description=__doc__,
+ packages=setuptools.find_packages(),
+ package_data={'pigweed_experimental_tools': ['py.typed']},
+ zip_safe=False,
+ entry_points={
+ 'console_scripts': [
+ 'find-files = pigweed_experimental_tools.find_files:main',
+ ]
+ },
+ install_requires=[
+ 'pw_cli',
+ ])