diff --git a/pico-light-voice b/pico-light-voice
deleted file mode 160000
index 5a82588..0000000
--- a/pico-light-voice
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 5a825884753207656080b091d87490348452670c
diff --git a/pico-light-voice/.gitignore b/pico-light-voice/.gitignore
new file mode 100644
index 0000000..02c448c
--- /dev/null
+++ b/pico-light-voice/.gitignore
@@ -0,0 +1,5 @@
+build/
+*.DS_Store
+edge-impulse-sdk/
+model-parameters/
+tflite-model/
\ No newline at end of file
diff --git a/pico-light-voice/CMakeLists.txt b/pico-light-voice/CMakeLists.txt
new file mode 100755
index 0000000..e497e57
--- /dev/null
+++ b/pico-light-voice/CMakeLists.txt
@@ -0,0 +1,88 @@
+cmake_minimum_required(VERSION 3.13.1)
+
+set(MODEL_FOLDER .)
+set(EI_SDK_FOLDER edge-impulse-sdk)
+
+include(pico_sdk_import.cmake)
+
+project(pico-voice C CXX ASM)
+set(CMAKE_C_STANDARD 11)
+set(CMAKE_CXX_STANDARD 11)
+
+pico_sdk_init()
+
+add_executable(pico-voice
+ source/main.cpp
+ )
+
+include(${MODEL_FOLDER}/edge-impulse-sdk/cmake/utils.cmake)
+
+# enable usb output, disable uart output
+pico_enable_stdio_usb(pico-voice 1)
+pico_enable_stdio_uart(pico-voice 0)
+
+target_include_directories(pico-voice PRIVATE
+ ${MODEL_FOLDER}
+ ${MODEL_FOLDER}/classifer
+ ${MODEL_FOLDER}/tflite-model
+ ${MODEL_FOLDER}/model-parameters
+ )
+
+target_include_directories(pico-voice PRIVATE
+ ${EI_SDK_FOLDER}
+ ${EI_SDK_FOLDER}/third_party/ruy
+ ${EI_SDK_FOLDER}/third_party/gemmlowp
+ ${EI_SDK_FOLDER}/third_party/flatbuffers/include
+ ${EI_SDK_FOLDER}/third_party
+ ${EI_SDK_FOLDER}/tensorflow
+ ${EI_SDK_FOLDER}/dsp
+ ${EI_SDK_FOLDER}/classifier
+ ${EI_SDK_FOLDER}/anomaly
+ ${EI_SDK_FOLDER}/CMSIS/NN/Include
+ ${EI_SDK_FOLDER}/CMSIS/DSP/PrivateInclude
+ ${EI_SDK_FOLDER}/CMSIS/DSP/Include
+ ${EI_SDK_FOLDER}/CMSIS/Core/Include
+ )
+
+include_directories(${INCLUDES})
+
+# find model source files
+RECURSIVE_FIND_FILE(MODEL_FILES "${MODEL_FOLDER}/tflite-model" "*.cpp")
+RECURSIVE_FIND_FILE(SOURCE_FILES "${EI_SDK_FOLDER}" "*.cpp")
+RECURSIVE_FIND_FILE(CC_FILES "${EI_SDK_FOLDER}" "*.cc")
+RECURSIVE_FIND_FILE(S_FILES "${EI_SDK_FOLDER}" "*.s")
+RECURSIVE_FIND_FILE(C_FILES "${EI_SDK_FOLDER}" "*.c")
+list(APPEND SOURCE_FILES ${S_FILES})
+list(APPEND SOURCE_FILES ${C_FILES})
+list(APPEND SOURCE_FILES ${CC_FILES})
+list(APPEND SOURCE_FILES ${MODEL_FILES})
+
+# add all sources to the project
+target_sources(pico-voice PRIVATE ${SOURCE_FILES})
+
+# now do Neopixel Library
+add_library(pico_neopixel INTERFACE)
+
+pico_generate_pio_header(pico_neopixel ${CMAKE_CURRENT_LIST_DIR}/pico_neopixels/ws2812byte.pio)
+
+target_sources(pico_neopixel INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/pico_neopixels/Adafruit_NeoPixel.cpp
+)
+
+pico_enable_stdio_usb(pico_neopixel 1)
+pico_enable_stdio_uart(pico_neopixel 0)
+
+target_include_directories(pico_neopixel INTERFACE ${CMAKE_CURRENT_LIST_DIR}/pico_neopixels/include)
+
+target_link_libraries(pico_neopixel INTERFACE pico_stdlib hardware_pio pico_malloc pico_mem_ops)
+
+# Finish up
+target_link_libraries(pico-voice
+ hardware_adc
+ hardware_dma
+ pico_stdlib
+ pico_neopixel
+ pico_multicore
+ )
+
+pico_add_extra_outputs(pico-voice)
diff --git a/pico-light-voice/LICENSE b/pico-light-voice/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/pico-light-voice/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/pico-light-voice/README.md b/pico-light-voice/README.md
new file mode 100644
index 0000000..515c7f1
--- /dev/null
+++ b/pico-light-voice/README.md
@@ -0,0 +1,15 @@
+# pico-light-voice1
+
+This program combines a voice recognition model with Neopixels to make a low-cost lighting controller. Much of the ML code is from Edge Impulse's pico [standalone repo](https://github.com/edgeimpulse/example-standalone-inferencing-pico), and the Neopixel code is from [this fantastic port](https://github.com/martinkooij/pi-pico-adafruit-neopixels) that converts the Neopixel routines to efficient PIO code.
+
+To use this starting point, you need to drop in the `edge-impulse-sdk`, `model-parameters`, and `tflite-model` folders generated when exporting your model for C++.
+
+## Program operation
+
+This code really pushes the Pico to its limits! Simultaneously it's sampling from the ADC, controlling Neopixels using PIO, doing ML processing on one core, and operating a lighting state machine on the second core. Pretty neat!
+
+## Modifications
+
+If you train your model on floating-point WAV files sampled at 5 kHz (see the pico-daq folder in this repository) then you shouldn't need to change much other than the results of the inferencing.
+
+If you trained your data on some other format, you will need to modify how data gets copied into the `features` buffer as well as things like the sample rate.
\ No newline at end of file
diff --git a/pico-light-voice/pico_neopixels/Adafruit_NeoPixel.cpp b/pico-light-voice/pico_neopixels/Adafruit_NeoPixel.cpp
new file mode 100644
index 0000000..81778b4
--- /dev/null
+++ b/pico-light-voice/pico_neopixels/Adafruit_NeoPixel.cpp
@@ -0,0 +1,712 @@
+/*!
+ * @file Adafruit_NeoPixel.cpp
+ *
+ * @mainpage Arduino Library for driving Adafruit NeoPixel addressable LEDs,
+ * FLORA RGB Smart Pixels and compatible devicess -- WS2811, WS2812, WS2812B,
+ * SK6812, etc.
+ *
+ * @section intro_sec Introduction
+ *
+ * This is the documentation for Adafruit's NeoPixel library for the
+ * Arduino platform, allowing a broad range of microcontroller boards
+ * (most AVR boards, many ARM devices, ESP8266 and ESP32, among others)
+ * to control Adafruit NeoPixels, FLORA RGB Smart Pixels and compatible
+ * devices -- WS2811, WS2812, WS2812B, SK6812, etc.
+ *
+ * Adafruit invests time and resources providing this open source code,
+ * please support Adafruit and open-source hardware by purchasing products
+ * from Adafruit!
+ *
+ * @section author Author
+ *
+ * Written by Phil "Paint Your Dragon" Burgess for Adafruit Industries,
+ * with contributions by PJRC, Michael Miller and other members of the
+ * open source community.
+ *
+ * @section license License
+ *
+ * This file is part of the Adafruit_NeoPixel library.
+ *
+ * Adafruit_NeoPixel is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * Adafruit_NeoPixel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with NeoPixel. If not, see
+ * .
+ *
+ */
+
+#include "Adafruit_NeoPixel.hpp"
+#include "pico/stdio.h"
+#include "pico/malloc.h"
+//#include "pico/mem_ops.h"
+#include
+#include
+#include
+
+//#define DEBUG0 // high level debugging
+//#define DEBUG1 // low level debugging
+
+#ifdef DEBUG0
+#define PRINTF0(...) printf(__VA_ARGS__)
+#else
+#define PRINTF0(...)
+#endif
+
+#ifdef DEBUG1
+#define PRINTF1(...) printf(__VA_ARGS__)
+#else
+#define PRINTF1(...)
+#endif
+
+
+/*!
+ @brief NeoPixel constructor when length, pin and pixel type are known
+ at compile-time.
+ @param n Number of NeoPixels in strand.
+ @param p Arduino pin number which will drive the NeoPixel data in.
+ @param t Pixel type -- add together NEO_* constants defined in
+ Adafruit_NeoPixel.h, for example NEO_GRB+NEO_KHZ800 for
+ NeoPixels expecting an 800 KHz (vs 400 KHz) data stream
+ with color bytes expressed in green, red, blue order per
+ pixel.
+ @return Adafruit_NeoPixel object. Call the begin() function before use.
+*/
+Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, uint16_t p, neoPixelType t) :
+ begun(false), brightness(0), pixels(NULL), opixels(NULL), brightfr(NULL), brightfg(NULL), brightfb(NULL), brightfw(NULL) {
+ PRINTF1("In constructor 1\n");
+ endTime = get_absolute_time() ;
+ PRINTF1("In constructor 2\n");
+ setPin(p);
+ PRINTF1("In constructor 3\n");
+ updateType(t);
+ PRINTF1("In constructor 4\n");
+ updateLength(n);
+
+}
+
+/*!
+ @brief "Empty" NeoPixel constructor when length, pin and/or pixel type
+ are not known at compile-time, and must be initialized later with
+ updateType(), updateLength() and setPin().
+ @return Adafruit_NeoPixel object. Call the begin() function before use.
+ @note This function is deprecated, here only for old projects that
+ may still be calling it. New projects should instead use the
+ 'new' keyword with the first constructor syntax (length, pin,
+ type).
+*/
+Adafruit_NeoPixel::Adafruit_NeoPixel() :
+#if defined(NEO_KHZ400)
+ is800KHz(true),
+#endif
+ begun(false), numLEDs(0), numBytes(0), pin(-1), brightness(0), pixels(NULL), opixels(NULL), brightfr(NULL), brightfg(NULL), brightfb(NULL), brightfw(NULL), rOffset(1), gOffset(0), bOffset(2), wOffset(1){
+ endTime = get_absolute_time();
+}
+
+/*!
+ @brief Deallocate Adafruit_NeoPixel object, set data pin back to INPUT, unclaim the statemachine
+*/
+Adafruit_NeoPixel::~Adafruit_NeoPixel() {
+ PRINTF0("In destructor\n ===>\n");
+ memset(pixels, 0, numBytes);
+ show() ;
+ sleep_ms(20) ;
+ PRINTF1("End init = %d, pin = %d, 800kHz = %d, length = %d, pio= %d, sm = %d, offset = %d, no_sm = [%d, %d]\n ", begun, pin, is800KHz, numLEDs, pio_get_index(pio), sm, (pio_get_index(pio) == 0) ? pio0_offset : pio1_offset,pio_no_sm[0], pio_no_sm[1] );
+ PRINTF1("going to free\n");
+ free(pixels); // unclaim the memory for the pixels
+ free(opixels); // unclaim the memory for the pixels
+ PRINTF1("freed pixels\n");
+ pio_sm_unclaim(pio,sm); // unclaim the state machine
+ pio_no_sm[pio_get_index(pio)]-- ;
+ if (pio_no_sm[pio_get_index(pio)] == 0 ) { // if no sm on the pio instance, remove the program
+ pio_remove_program (pio, &ws2812byte_program, (pio_get_index(pio) == 0) ? pio0_offset : pio1_offset );
+ if (pio_get_index(pio) == 0) {pio0_offset = -1;} else {pio1_offset = -1;};
+ };
+
+ PRINTF0("End destruructor = %d, pin = %d, 800kHz = %d, length = %d, pio= %d, sm = %d, offset = %d, no_sm = [%d, %d]\n ", begun, pin, is800KHz, numLEDs, pio_get_index(pio), sm, (pio_get_index(pio) == 0) ? pio0_offset : pio1_offset,pio_no_sm[0], pio_no_sm[1] );
+}
+
+/*!
+ @brief Configure NeoPixel pin for output.
+*/
+void Adafruit_NeoPixel::begin(void) {
+ // backwards comptability
+ // PRINTF0("In begin Begun = %d, pin = %d, length = %d\n", begun, pin, numLEDs);
+}
+
+/*!
+ @brief Change the length of a previously-declared Adafruit_NeoPixel
+ strip object. Old data is deallocated and new data is cleared.
+ Pin number and pixel format are unchanged.
+ @param n New length of strip, in pixels.
+ @note This function is deprecated, here only for old projects that
+ may still be calling it. New projects should instead use the
+ 'new' keyword with the first constructor syntax (length, pin,
+ type).
+*/
+void Adafruit_NeoPixel::updateLength(uint16_t n) {
+ free(pixels); // Free existing data (if any)
+
+ // Allocate new data -- note: ALL PIXELS ARE CLEARED
+ numBytes = n * ((wOffset == rOffset) ? 3 : 4);
+ if((pixels = (uint8_t *)malloc(numBytes))) {
+ memset(pixels, 0, numBytes);
+ numLEDs = n;
+ } else {
+ numLEDs = numBytes = 0;
+ }
+
+ if (brightfr != NULL) {
+ free(opixels) ;
+ if((opixels = (uint8_t *)malloc(numBytes))) {
+ memset(opixels, 0, numBytes);
+ numLEDs = n;
+ } else {
+ numLEDs = numBytes = 0;
+ };
+ };
+
+}
+
+/*!
+ @brief Change the pixel format of a previously-declared
+ Adafruit_NeoPixel strip object. If format changes from one of
+ the RGB variants to an RGBW variant (or RGBW to RGB), the old
+ data will be deallocated and new data is cleared. Otherwise,
+ the old data will remain in RAM and is not reordered to the
+ new format, so it's advisable to follow up with clear().
+ @param t Pixel type -- add together NEO_* constants defined in
+ Adafruit_NeoPixel.h, for example NEO_GRB+NEO_KHZ800 for
+ NeoPixels expecting an 800 KHz (vs 400 KHz) data stream
+ with color bytes expressed in green, red, blue order per
+ pixel.
+ @note This function is deprecated, here only for old projects that
+ may still be calling it. New projects should instead use the
+ 'new' keyword with the first constructor syntax
+ (length, pin, type).
+*/
+void Adafruit_NeoPixel::updateType(neoPixelType t) {
+ bool oldThreeBytesPerPixel = (wOffset == rOffset); // false if RGBW
+
+ wOffset = (t >> 6) & 0b11; // See notes in header file
+ rOffset = (t >> 4) & 0b11; // regarding R/G/B/W offsets
+ gOffset = (t >> 2) & 0b11;
+ bOffset = t & 0b11;
+#if defined(NEO_KHZ400)
+ is800KHz = (t < 256); // 400 KHz flag is 1<<8
+#endif
+
+ // If bytes-per-pixel has changed (and pixel data was previously
+ // allocated), re-allocate to new size. Will clear any data.
+ if(pixels) {
+ bool newThreeBytesPerPixel = (wOffset == rOffset);
+ if(newThreeBytesPerPixel != oldThreeBytesPerPixel) updateLength(numLEDs);
+ }
+}
+
+
+void Adafruit_NeoPixel::rp2040Init(uint8_t set_pin)
+{
+ PRINTF0("IN RP2040 INIT now\n");
+ // get free sm & pio and store these in the protected variables of the class
+ int resultsm;
+ bool canpio;
+ resultsm = pio_claim_unused_sm(pio0,false);
+ PRINTF1("resultsm = %d\n",resultsm);
+ if (resultsm != -1) {
+ if (pio0_offset == -1) {
+ canpio = pio_can_add_program(pio0, &ws2812byte_program);
+ PRINTF1("canpio = %d\n",canpio);
+ if (canpio) pio0_offset = pio_add_program(pio0, &ws2812byte_program);
+ };
+ pio = pio0;
+ };
+ if (resultsm == -1 || pio0_offset == -1) {
+ resultsm = pio_claim_unused_sm(pio1,false);
+ if (resultsm != -1) {
+ if (pio1_offset == -1) {
+ canpio = pio_can_add_program(pio1, &ws2812byte_program);
+ if (canpio) pio1_offset = pio_add_program(pio0, &ws2812byte_program);
+ };
+ pio = pio1;
+ }
+ };
+
+ if (resultsm == -1 || (pio0_offset == -1 && pio1_offset == -1)) {
+ sm = -1 ;
+ return ;
+ }
+
+ pio_no_sm[pio_get_index(pio)]++ ;
+
+ pin = set_pin ;
+ sm = resultsm ;
+
+ PRINTF1("End init = %d, pin = %d, 800kHz = %d, length = %d, pio= %d, sm = %d, offset = %d, no_sm = [%d, %d]\n ", begun, pin, is800KHz, numLEDs, pio_get_index(pio), sm, (pio_get_index(pio) == 0) ? pio0_offset : pio1_offset,pio_no_sm[0], pio_no_sm[1] );
+
+ if (is800KHz)
+ {
+ // 800kHz, 8 bit transfers
+ ws2812byte_program_init(pio, sm, (pio_get_index(pio) == 0) ? pio0_offset : pio1_offset, pin, 800000, 8);
+ }
+ else
+ {
+ // 400kHz, 8 bit transfers
+ ws2812byte_program_init(pio, sm, (pio_get_index(pio) == 0) ? pio0_offset : pio1_offset, pin, 400000, 8);
+ } ;
+ begun = true ;
+ PRINTF0("exit INIT pio %d, sm %d\n", pio_get_index(pio),sm);
+}
+
+void Adafruit_NeoPixel::rp2040changepin(uint8_t set_pin)
+{
+ pin = set_pin ;
+ int resultpio = pio_get_index(pio);
+
+ if (is800KHz)
+ {
+ // 800kHz, 8 bit transfers
+ ws2812byte_program_init(pio, sm, (resultpio == 0) ? pio0_offset : pio1_offset, pin, 800000, 8);
+ }
+ else
+ {
+ // 400kHz, 8 bit transfers
+ ws2812byte_program_init(pio, sm, (resultpio == 0) ? pio0_offset : pio1_offset, pin, 400000, 8);
+ }
+}
+
+void Adafruit_NeoPixel::rp2040Show(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz)
+{
+ PRINTF0("In Show,");
+ if (!begun)
+ {
+ // On first pass through initialise the PIO
+ rp2040Init(pin);
+ begun = true ;
+ }
+
+ if (sm == -1) { return ; }
+
+// PRINTF1("START TO SHOW = %d, pin = %d, 800kHz = %d, length = %d, pio= %d, sm = %d, offset = %d, no_sm = [%d, %d]\n ", begun, pin, is800KHz, numLEDs, pio_get_index(pio), sm, (pio_get_index(pio) == 0) ? pio0_offset : pio1_offset,pio_no_sm[0], pio_no_sm[1] );
+ while(numBytes--)
+ // Bits for transmission must be shifted to top 8 bits
+ pio_sm_put_blocking(pio, sm, ((uint32_t)*pixels++)<< 24);
+}
+
+
+/*!
+ @brief Transmit pixel data in RAM to NeoPixels.
+ @note On most architectures, interrupts are temporarily disabled in
+ order to achieve the correct NeoPixel signal timing. This means
+ that the Arduino millis() and micros() functions, which require
+ interrupts, will lose small intervals of time whenever this
+ function is called (about 30 microseconds per RGB pixel, 40 for
+ RGBW pixels). There's no easy fix for this, but a few
+ specialized alternative or companion libraries exist that use
+ very device-specific peripherals to work around it.
+*/
+void Adafruit_NeoPixel::show(void) {
+
+ if(!pixels) return;
+
+ rp2040Show(pin, pixels, numBytes, is800KHz);
+
+}
+
+/*!
+ @brief Set/change the NeoPixel output pin number. Previous pin,
+ if any, is set to INPUT and the new pin is set to OUTPUT.
+ @param p pin number.
+*/
+void Adafruit_NeoPixel::setPin(uint16_t p) {
+ if (!begun) {
+ pin = p ;
+ return;
+ };
+ rp2040changepin(pin);
+}
+
+/*!
+ @brief Set a pixel's color using separate red, green and blue
+ components. If using RGBW pixels, white will be set to 0.
+ @param n Pixel index, starting from 0.
+ @param r Red brightness, 0 = minimum (off), 255 = maximum.
+ @param g Green brightness, 0 = minimum (off), 255 = maximum.
+ @param b Blue brightness, 0 = minimum (off), 255 = maximum.
+*/
+void Adafruit_NeoPixel::setPixelColor(
+ uint16_t n, uint8_t r, uint8_t g, uint8_t b) {
+ setPixelColor(n,r,g,b,0);
+}
+
+/*!
+ @brief Set a pixel's color using separate red, green, blue and white
+ components (for RGBW NeoPixels only).
+ @param n Pixel index, starting from 0.
+ @param r Red brightness, 0 = minimum (off), 255 = maximum.
+ @param g Green brightness, 0 = minimum (off), 255 = maximum.
+ @param b Blue brightness, 0 = minimum (off), 255 = maximum.
+ @param w White brightness, 0 = minimum (off), 255 = maximum, ignored
+ if using RGB pixels.
+*/
+void Adafruit_NeoPixel::setPixelColor(
+ uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
+
+ if(n < numLEDs) {
+ if(brightness) { // See notes in setBrightness()
+ r = (r * brightness) >> 8;
+ g = (g * brightness) >> 8;
+ b = (b * brightness) >> 8;
+ w = (w * brightness) >> 8;
+ }
+ uint8_t *p, *po;
+ if (brightfr == NULL) {
+ if(wOffset == rOffset) { // Is an RGB-type strip
+ p = &pixels[n * 3]; // 3 bytes per pixel
+ } else { // Is a WRGB-type strip
+ p = &pixels[n * 4]; // 4 bytes per pixel
+ p[wOffset] = w; // set W
+ }
+ p[rOffset] = r; // R,G,B always stored
+ p[gOffset] = g;
+ p[bOffset] = b;
+ } else {
+ if(wOffset == rOffset) { // Is an RGB-type strip
+ po = &opixels[n * 3]; // 3 bytes per pixel
+ p = &pixels[n * 3];
+ } else { // Is a WRGB-type strip
+ po = &opixels[n * 4]; // 4 bytes per pixel
+ p = &pixels[n * 4];
+ po[wOffset] = w; // set W
+ p[wOffset] = brightfw(w);
+ }
+ po[rOffset] = r; // R,G,B always stored
+ po[gOffset] = g;
+ po[bOffset] = b;
+ p[rOffset] = brightfr(r);
+ p[gOffset] = brightfg(g);
+ p[bOffset] = brightfb(b);
+ }
+
+ }
+}
+
+/*!
+ @brief Set a pixel's color using a 32-bit 'packed' RGB or RGBW value.
+ @param n Pixel index, starting from 0.
+ @param c 32-bit color value. Most significant byte is white (for RGBW
+ pixels) or ignored (for RGB pixels), next is red, then green,
+ and least significant byte is blue.
+*/
+void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) {
+ uint8_t w = (uint8_t)(c >> 24);
+ uint8_t r = (uint8_t)(c >> 16);
+ uint8_t g = (uint8_t)(c >> 8);
+ uint8_t b = (uint8_t)c;
+ setPixelColor(n,r,g,b,w);
+}
+
+/*!
+ @brief Fill all or part of the NeoPixel strip with a color.
+ @param c 32-bit color value. Most significant byte is white (for
+ RGBW pixels) or ignored (for RGB pixels), next is red,
+ then green, and least significant byte is blue. If all
+ arguments are unspecified, this will be 0 (off).
+ @param first Index of first pixel to fill, starting from 0. Must be
+ in-bounds, no clipping is performed. 0 if unspecified.
+ @param count Number of pixels to fill, as a positive value. Passing
+ 0 or leaving unspecified will fill to end of strip.
+*/
+void Adafruit_NeoPixel::fill(uint32_t c, uint16_t first, uint16_t count) {
+ uint16_t i, end;
+
+ if(first >= numLEDs) {
+ return; // If first LED is past end of strip, nothing to do
+ }
+
+ // Calculate the index ONE AFTER the last pixel to fill
+ if(count == 0) {
+ // Fill to end of strip
+ end = numLEDs;
+ } else {
+ // Ensure that the loop won't go past the last pixel
+ end = first + count;
+ if(end > numLEDs) end = numLEDs;
+ }
+
+ for(i = first; i < end; i++) {
+ this->setPixelColor(i, c);
+ }
+}
+
+/*!
+ @brief Convert hue, saturation and value into a packed 32-bit RGB color
+ that can be passed to setPixelColor() or other RGB-compatible
+ functions.
+ @param hue An unsigned 16-bit value, 0 to 65535, representing one full
+ loop of the color wheel, which allows 16-bit hues to "roll
+ over" while still doing the expected thing (and allowing
+ more precision than the wheel() function that was common to
+ prior NeoPixel examples).
+ @param sat Saturation, 8-bit value, 0 (min or pure grayscale) to 255
+ (max or pure hue). Default of 255 if unspecified.
+ @param val Value (brightness), 8-bit value, 0 (min / black / off) to
+ 255 (max or full brightness). Default of 255 if unspecified.
+ @return Packed 32-bit RGB with the most significant byte set to 0 -- the
+ white element of WRGB pixels is NOT utilized. Result is linearly
+ but not perceptually correct, so you may want to pass the result
+ through the gamma32() function (or your own gamma-correction
+ operation) else colors may appear washed out. This is not done
+ automatically by this function because coders may desire a more
+ refined gamma-correction function than the simplified
+ one-size-fits-all operation of gamma32(). Diffusing the LEDs also
+ really seems to help when using low-saturation colors.
+*/
+uint32_t Adafruit_NeoPixel::ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) {
+
+ uint8_t r, g, b;
+
+ // Remap 0-65535 to 0-1529. Pure red is CENTERED on the 64K rollover;
+ // 0 is not the start of pure red, but the midpoint...a few values above
+ // zero and a few below 65536 all yield pure red (similarly, 32768 is the
+ // midpoint, not start, of pure cyan). The 8-bit RGB hexcone (256 values
+ // each for red, green, blue) really only allows for 1530 distinct hues
+ // (not 1536, more on that below), but the full unsigned 16-bit type was
+ // chosen for hue so that one's code can easily handle a contiguous color
+ // wheel by allowing hue to roll over in either direction.
+ hue = (hue * 1530L + 32768) / 65536;
+ // Because red is centered on the rollover point (the +32768 above,
+ // essentially a fixed-point +0.5), the above actually yields 0 to 1530,
+ // where 0 and 1530 would yield the same thing. Rather than apply a
+ // costly modulo operator, 1530 is handled as a special case below.
+
+ // So you'd think that the color "hexcone" (the thing that ramps from
+ // pure red, to pure yellow, to pure green and so forth back to red,
+ // yielding six slices), and with each color component having 256
+ // possible values (0-255), might have 1536 possible items (6*256),
+ // but in reality there's 1530. This is because the last element in
+ // each 256-element slice is equal to the first element of the next
+ // slice, and keeping those in there this would create small
+ // discontinuities in the color wheel. So the last element of each
+ // slice is dropped...we regard only elements 0-254, with item 255
+ // being picked up as element 0 of the next slice. Like this:
+ // Red to not-quite-pure-yellow is: 255, 0, 0 to 255, 254, 0
+ // Pure yellow to not-quite-pure-green is: 255, 255, 0 to 1, 255, 0
+ // Pure green to not-quite-pure-cyan is: 0, 255, 0 to 0, 255, 254
+ // and so forth. Hence, 1530 distinct hues (0 to 1529), and hence why
+ // the constants below are not the multiples of 256 you might expect.
+
+ // Convert hue to R,G,B (nested ifs faster than divide+mod+switch):
+ if(hue < 510) { // Red to Green-1
+ b = 0;
+ if(hue < 255) { // Red to Yellow-1
+ r = 255;
+ g = hue; // g = 0 to 254
+ } else { // Yellow to Green-1
+ r = 510 - hue; // r = 255 to 1
+ g = 255;
+ }
+ } else if(hue < 1020) { // Green to Blue-1
+ r = 0;
+ if(hue < 765) { // Green to Cyan-1
+ g = 255;
+ b = hue - 510; // b = 0 to 254
+ } else { // Cyan to Blue-1
+ g = 1020 - hue; // g = 255 to 1
+ b = 255;
+ }
+ } else if(hue < 1530) { // Blue to Red-1
+ g = 0;
+ if(hue < 1275) { // Blue to Magenta-1
+ r = hue - 1020; // r = 0 to 254
+ b = 255;
+ } else { // Magenta to Red-1
+ r = 255;
+ b = 1530 - hue; // b = 255 to 1
+ }
+ } else { // Last 0.5 Red (quicker than % operator)
+ r = 255;
+ g = b = 0;
+ }
+
+ // Apply saturation and value to R,G,B, pack into 32-bit result:
+ uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255
+ uint16_t s1 = 1 + sat; // 1 to 256; same reason
+ uint8_t s2 = 255 - sat; // 255 to 0
+ return ((((((r * s1) >> 8) + s2) * v1) & 0xff00) << 8) |
+ (((((g * s1) >> 8) + s2) * v1) & 0xff00) |
+ ( ((((b * s1) >> 8) + s2) * v1) >> 8);
+}
+
+/*!
+ @brief Query the color of a previously-set pixel.
+ @param n Index of pixel to read (0 = first).
+ @return 'Packed' 32-bit RGB or WRGB value. Most significant byte is white
+ (for RGBW pixels) or 0 (for RGB pixels), next is red, then green,
+ and least significant byte is blue.
+ @note If the strip brightness has been changed from the default value
+ of 255, the color read from a pixel may not exactly match what
+ was previously written with one of the setPixelColor() functions.
+ This gets more pronounced at lower brightness levels.
+*/
+uint32_t Adafruit_NeoPixel::getPixelColor(uint16_t n) const {
+ if(n >= numLEDs) return 0; // Out of bounds, return no color.
+
+ uint8_t *p, *po;
+
+ if (brightfr == NULL) {
+ if(wOffset == rOffset) { // Is RGB-type device
+ p = &pixels[n * 3];
+ if(brightness) {
+ // Stored color was decimated by setBrightness(). Returned value
+ // attempts to scale back to an approximation of the original 24-bit
+ // value used when setting the pixel color, but there will always be
+ // some error -- those bits are simply gone. Issue is most
+ // pronounced at low brightness levels.
+ return (((uint32_t)(p[rOffset] << 8) / brightness) << 16) |
+ (((uint32_t)(p[gOffset] << 8) / brightness) << 8) |
+ ( (uint32_t)(p[bOffset] << 8) / brightness );
+ } else {
+ // No brightness adjustment has been made -- return 'raw' color
+ return ((uint32_t)p[rOffset] << 16) |
+ ((uint32_t)p[gOffset] << 8) |
+ (uint32_t)p[bOffset];
+ }
+ } else { // Is RGBW-type device
+ p = &pixels[n * 4];
+ if(brightness) { // Return scaled color
+ return (((uint32_t)(p[wOffset] << 8) / brightness) << 24) |
+ (((uint32_t)(p[rOffset] << 8) / brightness) << 16) |
+ (((uint32_t)(p[gOffset] << 8) / brightness) << 8) |
+ ( (uint32_t)(p[bOffset] << 8) / brightness );
+ } else { // Return raw color
+ return ((uint32_t)p[wOffset] << 24) |
+ ((uint32_t)p[rOffset] << 16) |
+ ((uint32_t)p[gOffset] << 8) |
+ (uint32_t)p[bOffset];
+ }
+ }
+ } else { // else take the values from the original array
+ if(wOffset == rOffset) { // Is RGB-type device
+ po = &opixels[n * 3];
+ return ((uint32_t)po[rOffset] << 16) |
+ ((uint32_t)po[gOffset] << 8) |
+ (uint32_t)po[bOffset];
+ } else { // Is RGBW-type device
+ po = &opixels[n * 4];
+ return ((uint32_t)po[wOffset] << 24) |
+ ((uint32_t)po[rOffset] << 16) |
+ ((uint32_t)po[gOffset] << 8) |
+ (uint32_t)po[bOffset];
+ }
+ }
+}
+
+
+/*!
+ @brief Adjust output brightness. Does not immediately affect what's
+ currently displayed on the LEDs. The next call to show() will
+ refresh the LEDs at this level.
+ @param b Brightness setting, 0=minimum (off), 255=brightest.
+ @note This was intended for one-time use in one's setup() function,
+ not as an animation effect in itself. Because of the way this
+ library "pre-multiplies" LED colors in RAM, changing the
+ brightness is often a "lossy" operation -- what you write to
+ pixels isn't necessary the same as what you'll read back.
+ Repeated brightness changes using this function exacerbate the
+ problem. Smart programs therefore treat the strip as a
+ write-only resource, maintaining their own state to render each
+ frame of an animation, not relying on read-modify-write.
+*/
+void Adafruit_NeoPixel::setBrightness(uint8_t b) {
+ // Stored brightness value is different than what's passed.
+ // This simplifies the actual scaling math later, allowing a fast
+ // 8x8-bit multiply and taking the MSB. 'brightness' is a uint8_t,
+ // adding 1 here may (intentionally) roll over...so 0 = max brightness
+ // (color values are interpreted literally; no scaling), 1 = min
+ // brightness (off), 255 = just below max brightness.
+ uint8_t newBrightness = b + 1;
+ if(newBrightness != brightness) { // Compare against prior value
+ // Brightness has changed -- re-scale existing data in RAM,
+ // This process is potentially "lossy," especially when increasing
+ // brightness. The tight timing in the WS2811/WS2812 code means there
+ // aren't enough free cycles to perform this scaling on the fly as data
+ // is issued. So we make a pass through the existing color data in RAM
+ // and scale it (subsequent graphics commands also work at this
+ // brightness level). If there's a significant step up in brightness,
+ // the limited number of steps (quantization) in the old data will be
+ // quite visible in the re-scaled version. For a non-destructive
+ // change, you'll need to re-render the full strip data. C'est la vie.
+ uint8_t c,
+ *ptr = pixels,
+ oldBrightness = brightness - 1; // De-wrap old brightness value
+ uint16_t scale;
+ if(oldBrightness == 0) scale = 0; // Avoid /0
+ else if(b == 255) scale = 65535 / oldBrightness;
+ else scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;
+ for(uint16_t i=0; i> 8;
+ }
+ brightness = newBrightness;
+ }
+}
+
+void Adafruit_NeoPixel::setBrightnessFunctions(pBrightnessFunc fr, pBrightnessFunc fg, pBrightnessFunc fb, pBrightnessFunc fw) {
+
+ if (opixels == NULL & numLEDs != 0) {
+ opixels = (uint8_t *)malloc(numBytes);
+ memcpy(opixels,pixels,numBytes);
+ }
+
+ brightfr = fr;
+ brightfg = fg;
+ brightfb = fb;
+ brightfw = fw;
+
+ for (int i = 0 ; i < numLEDs ; i++) {
+ uint32_t pixel;
+ pixel = getPixelColor(i);
+ setPixelColor(i,pixel);
+ }
+};
+
+
+
+/*!
+ @brief Retrieve the last-set brightness value for the strip.
+ @return Brightness value: 0 = minimum (off), 255 = maximum.
+*/
+uint8_t Adafruit_NeoPixel::getBrightness(void) const {
+ return brightness - 1;
+}
+
+/*!
+ @brief Fill the whole NeoPixel strip with 0 / black / off.
+*/
+void Adafruit_NeoPixel::clear(void) {
+ memset(pixels, 0, numBytes);
+}
+
+// A 32-bit variant of gamma8() that applies the same function
+// to all components of a packed RGB or WRGB value.
+uint32_t Adafruit_NeoPixel::gamma32(uint32_t x) {
+ uint8_t *y = (uint8_t *)&x;
+ // All four bytes of a 32-bit value are filtered even if RGB (not WRGB),
+ // to avoid a bunch of shifting and masking that would be necessary for
+ // properly handling different endianisms (and each byte is a fairly
+ // trivial operation, so it might not even be wasting cycles vs a check
+ // and branch for the RGB case). In theory this might cause trouble *if*
+ // someone's storing information in the unused most significant byte
+ // of an RGB value, but this seems exceedingly rare and if it's
+ // encountered in reality they can mask values going in or coming out.
+ for(uint8_t i=0; i<4; i++) y[i] = gamma8(y[i]);
+ return x; // Packed 32-bit return
+}
diff --git a/pico-light-voice/pico_neopixels/CMakeLists.txt b/pico-light-voice/pico_neopixels/CMakeLists.txt
new file mode 100644
index 0000000..52dfe21
--- /dev/null
+++ b/pico-light-voice/pico_neopixels/CMakeLists.txt
@@ -0,0 +1,15 @@
+add_library(pico_neopixel INTERFACE)
+
+pico_generate_pio_header(pico_neopixel ${CMAKE_CURRENT_LIST_DIR}/ws2812byte.pio)
+
+target_sources(pico_neopixel INTERFACE
+ ${CMAKE_CURRENT_LIST_DIR}/Adafruit_NeoPixel.cpp
+)
+
+pico_enable_stdio_usb(pico_neopixel 1)
+pico_enable_stdio_uart(pico_neopixel 0)
+
+target_include_directories(pico_neopixel INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
+
+# Pull in pico libraries that we need
+target_link_libraries(pico_neopixel INTERFACE pico_stdlib hardware_pio pico_malloc pico_mem_ops)
\ No newline at end of file
diff --git a/pico-light-voice/pico_neopixels/include/Adafruit_NeoPixel.h b/pico-light-voice/pico_neopixels/include/Adafruit_NeoPixel.h
new file mode 100644
index 0000000..e216303
--- /dev/null
+++ b/pico-light-voice/pico_neopixels/include/Adafruit_NeoPixel.h
@@ -0,0 +1,350 @@
+/*!
+ * @file Adafruit_NeoPixel.h
+ *
+ * This is part of Adafruit's NeoPixel library for the Arduino platform,
+ * allowing a broad range of microcontroller boards (most AVR boards,
+ * many ARM devices, ESP8266 and ESP32, among others) to control Adafruit
+ * NeoPixels, FLORA RGB Smart Pixels and compatible devices -- WS2811,
+ * WS2812, WS2812B, SK6812, etc.
+ *
+ * Adafruit invests time and resources providing this open source code,
+ * please support Adafruit and open-source hardware by purchasing products
+ * from Adafruit!
+ *
+ * Written by Phil "Paint Your Dragon" Burgess for Adafruit Industries,
+ * with contributions by PJRC, Michael Miller and other members of the
+ * open source community.
+ *
+ * This file is part of the Adafruit_NeoPixel library.
+ *
+ * Adafruit_NeoPixel is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * Adafruit_NeoPixel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with NeoPixel. If not, see
+ * .
+ *
+ */
+
+
+#pragma once
+#include "Adafruit_NeoPixel.h"
+#include "pico/stdlib"
+#include "hardware/pio"
+#include "pico/time"
+#include "ws2812byte.pio.h"
+
+
+
+// The order of primary colors in the NeoPixel data stream can vary among
+// device types, manufacturers and even different revisions of the same
+// item. The third parameter to the Adafruit_NeoPixel constructor encodes
+// the per-pixel byte offsets of the red, green and blue primaries (plus
+// white, if present) in the data stream -- the following #defines provide
+// an easier-to-use named version for each permutation. e.g. NEO_GRB
+// indicates a NeoPixel-compatible device expecting three bytes per pixel,
+// with the first byte transmitted containing the green value, second
+// containing red and third containing blue. The in-memory representation
+// of a chain of NeoPixels is the same as the data-stream order; no
+// re-ordering of bytes is required when issuing data to the chain.
+// Most of these values won't exist in real-world devices, but it's done
+// this way so we're ready for it (also, if using the WS2811 driver IC,
+// one might have their pixels set up in any weird permutation).
+
+// Bits 5,4 of this value are the offset (0-3) from the first byte of a
+// pixel to the location of the red color byte. Bits 3,2 are the green
+// offset and 1,0 are the blue offset. If it is an RGBW-type device
+// (supporting a white primary in addition to R,G,B), bits 7,6 are the
+// offset to the white byte...otherwise, bits 7,6 are set to the same value
+// as 5,4 (red) to indicate an RGB (not RGBW) device.
+// i.e. binary representation:
+// 0bWWRRGGBB for RGBW devices
+// 0bRRRRGGBB for RGB
+
+// RGB NeoPixel permutations; white and red offsets are always same
+// Offset: W R G B
+#define NEO_RGB ((0<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B
+#define NEO_RBG ((0<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G
+#define NEO_GRB ((1<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B
+#define NEO_GBR ((2<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R
+#define NEO_BRG ((1<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G
+#define NEO_BGR ((2<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R
+
+// RGBW NeoPixel permutations; all 4 offsets are distinct
+// Offset: W R G B
+#define NEO_WRGB ((0<<6) | (1<<4) | (2<<2) | (3)) ///< Transmit as W,R,G,B
+#define NEO_WRBG ((0<<6) | (1<<4) | (3<<2) | (2)) ///< Transmit as W,R,B,G
+#define NEO_WGRB ((0<<6) | (2<<4) | (1<<2) | (3)) ///< Transmit as W,G,R,B
+#define NEO_WGBR ((0<<6) | (3<<4) | (1<<2) | (2)) ///< Transmit as W,G,B,R
+#define NEO_WBRG ((0<<6) | (2<<4) | (3<<2) | (1)) ///< Transmit as W,B,R,G
+#define NEO_WBGR ((0<<6) | (3<<4) | (2<<2) | (1)) ///< Transmit as W,B,G,R
+
+#define NEO_RWGB ((1<<6) | (0<<4) | (2<<2) | (3)) ///< Transmit as R,W,G,B
+#define NEO_RWBG ((1<<6) | (0<<4) | (3<<2) | (2)) ///< Transmit as R,W,B,G
+#define NEO_RGWB ((2<<6) | (0<<4) | (1<<2) | (3)) ///< Transmit as R,G,W,B
+#define NEO_RGBW ((3<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B,W
+#define NEO_RBWG ((2<<6) | (0<<4) | (3<<2) | (1)) ///< Transmit as R,B,W,G
+#define NEO_RBGW ((3<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G,W
+
+#define NEO_GWRB ((1<<6) | (2<<4) | (0<<2) | (3)) ///< Transmit as G,W,R,B
+#define NEO_GWBR ((1<<6) | (3<<4) | (0<<2) | (2)) ///< Transmit as G,W,B,R
+#define NEO_GRWB ((2<<6) | (1<<4) | (0<<2) | (3)) ///< Transmit as G,R,W,B
+#define NEO_GRBW ((3<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B,W
+#define NEO_GBWR ((2<<6) | (3<<4) | (0<<2) | (1)) ///< Transmit as G,B,W,R
+#define NEO_GBRW ((3<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R,W
+
+#define NEO_BWRG ((1<<6) | (2<<4) | (3<<2) | (0)) ///< Transmit as B,W,R,G
+#define NEO_BWGR ((1<<6) | (3<<4) | (2<<2) | (0)) ///< Transmit as B,W,G,R
+#define NEO_BRWG ((2<<6) | (1<<4) | (3<<2) | (0)) ///< Transmit as B,R,W,G
+#define NEO_BRGW ((3<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G,W
+#define NEO_BGWR ((2<<6) | (3<<4) | (1<<2) | (0)) ///< Transmit as B,G,W,R
+#define NEO_BGRW ((3<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R,W
+
+// Add NEO_KHZ400 to the color order value to indicate a 400 KHz device.
+// All but the earliest v1 NeoPixels expect an 800 KHz data stream, this is
+// the default if unspecified. Because flash space is very limited on ATtiny
+// devices (e.g. Trinket, Gemma), v1 NeoPixels aren't handled by default on
+// those chips, though it can be enabled by removing the ifndef/endif below,
+// but code will be bigger. Conversely, can disable the NEO_KHZ400 line on
+// other MCUs to remove v1 support and save a little space.
+
+
+#define NEO_KHZ800 0x0000 ///< 800 KHz data transmission
+#define NEO_KHZ400 0x0100 ///< 400 KHz data transmission
+
+typedef uint16_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor
+
+// These two tables are declared outside the Adafruit_NeoPixel class
+// because some boards may require oldschool compilers that don't
+// handle the C++11 constexpr keyword.
+
+/* A PROGMEM (flash mem) table containing 8-bit unsigned sine wave (0-255).
+ Copy & paste this snippet into a Python REPL to regenerate:
+import math
+for x in range(256):
+ print("{:3},".format(int((math.sin(x/128.0*math.pi)+1.0)*127.5+0.5))),
+ if x&15 == 15: print
+*/
+static const uint8_t _NeoPixelSineTable[256] = {
+ 128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173,
+ 176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215,
+ 218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244,
+ 245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255,
+ 255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246,
+ 245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220,
+ 218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179,
+ 176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131,
+ 128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82,
+ 79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40,
+ 37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11,
+ 10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9,
+ 10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35,
+ 37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76,
+ 79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124};
+
+/* Similar to above, but for an 8-bit gamma-correction table.
+ Copy & paste this snippet into a Python REPL to regenerate:
+import math
+gamma=2.6
+for x in range(256):
+ print("{:3},".format(int(math.pow((x)/255.0,gamma)*255.0+0.5))),
+ if x&15 == 15: print
+*/
+static const uint8_t _NeoPixelGammaTable[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
+ 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7,
+ 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12,
+ 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20,
+ 20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29,
+ 30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 75,
+ 76, 77, 78, 80, 81, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96,
+ 97, 99,100,102,103,105,106,108,109,111,112,114,115,117,119,120,
+ 122,124,125,127,129,130,132,134,136,137,139,141,143,145,146,148,
+ 150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,
+ 182,184,186,188,191,193,195,197,199,202,204,206,209,211,213,215,
+ 218,220,223,225,227,230,232,235,237,240,242,245,247,250,252,255};
+
+// gloal hardware resource variables keeping track of configurations an usage on the pi Pico RP2040.
+static int pio0_offset = -1; // offset of loaded pio neopixel program on pio0; -1 if no program loaded
+static int pio1_offset = -1; // offset of loaded pio neopixel program on pio1; -1 if no program loaded
+static int pio_no_sm[2] = {0,0} ; // number of state machines in use for Neopixel
+
+
+/*!
+ @brief Class that stores state and functions for interacting with
+ Adafruit NeoPixels and compatible devices.
+*/
+class Adafruit_NeoPixel {
+
+ public:
+
+ // Constructor: number of LEDs, pin number, LED type
+ Adafruit_NeoPixel(uint16_t n, uint16_t pin=0,
+ neoPixelType type=NEO_GRB + NEO_KHZ800);
+ Adafruit_NeoPixel(void);
+ ~Adafruit_NeoPixel();
+
+ void begin(void);
+ void show(void);
+ void setPin(uint16_t p);
+ void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b);
+ void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b,
+ uint8_t w);
+ void setPixelColor(uint16_t n, uint32_t c);
+ void fill(uint32_t c=0, uint16_t first=0, uint16_t count=0);
+ void setBrightness(uint8_t);
+ void clear(void);
+ void updateLength(uint16_t n);
+ void updateType(neoPixelType t);
+ /*!
+ @brief Check whether a call to show() will start sending data
+ immediately or will 'block' for a required interval. NeoPixels
+ require a short quiet time (about 300 microseconds) after the
+ last bit is received before the data 'latches' and new data can
+ start being received. Usually one's sketch is implicitly using
+ this time to generate a new frame of animation...but if it
+ finishes very quickly, this function could be used to see if
+ there's some idle time available for some low-priority
+ concurrent task.
+ @return 1 or true if show() will start sending immediately, 0 or false
+ if show() would block (meaning some idle time is available).
+ */
+ bool canShow(void) {
+ int64_t howlongago = absolute_time_diff_us (endTime, get_absolute_time());
+ return (howlongago >= 300L);
+ }
+ /*!
+ @brief Get a pointer directly to the NeoPixel data buffer in RAM.
+ Pixel data is stored in a device-native format (a la the NEO_*
+ constants) and is not translated here. Applications that access
+ this buffer will need to be aware of the specific data format
+ and handle colors appropriately.
+ @return Pointer to NeoPixel buffer (uint8_t* array).
+ @note This is for high-performance applications where calling
+ setPixelColor() on every single pixel would be too slow (e.g.
+ POV or light-painting projects). There is no bounds checking
+ on the array, creating tremendous potential for mayhem if one
+ writes past the ends of the buffer. Great power, great
+ responsibility and all that.
+ */
+ uint8_t *getPixels(void) const { return pixels; };
+ uint8_t getBrightness(void) const;
+ /*!
+ @brief Retrieve the pin number used for NeoPixel data output.
+ @return Arduino pin number (-1 if not set).
+ */
+ int16_t getPin(void) const { return pin; };
+ /*!
+ @brief Return the number of pixels in an Adafruit_NeoPixel strip object.
+ @return Pixel count (0 if not set).
+ */
+ uint16_t numPixels(void) const { return numLEDs; }
+ uint32_t getPixelColor(uint16_t n) const;
+ /*!
+ @brief An 8-bit integer sine wave function, not directly compatible
+ with standard trigonometric units like radians or degrees.
+ @param x Input angle, 0-255; 256 would loop back to zero, completing
+ the circle (equivalent to 360 degrees or 2 pi radians).
+ One can therefore use an unsigned 8-bit variable and simply
+ add or subtract, allowing it to overflow/underflow and it
+ still does the expected contiguous thing.
+ @return Sine result, 0 to 255, or -128 to +127 if type-converted to
+ a signed int8_t, but you'll most likely want unsigned as this
+ output is often used for pixel brightness in animation effects.
+ */
+ static uint8_t sine8(uint8_t x) {
+ return pgm_read_byte(&_NeoPixelSineTable[x]); // 0-255 in, 0-255 out
+ }
+ /*!
+ @brief An 8-bit gamma-correction function for basic pixel brightness
+ adjustment. Makes color transitions appear more perceptially
+ correct.
+ @param x Input brightness, 0 (minimum or off/black) to 255 (maximum).
+ @return Gamma-adjusted brightness, can then be passed to one of the
+ setPixelColor() functions. This uses a fixed gamma correction
+ exponent of 2.6, which seems reasonably okay for average
+ NeoPixels in average tasks. If you need finer control you'll
+ need to provide your own gamma-correction function instead.
+ */
+ static uint8_t gamma8(uint8_t x) {
+ return pgm_read_byte(&_NeoPixelGammaTable[x]); // 0-255 in, 0-255 out
+ }
+ /*!
+ @brief Convert separate red, green and blue values into a single
+ "packed" 32-bit RGB color.
+ @param r Red brightness, 0 to 255.
+ @param g Green brightness, 0 to 255.
+ @param b Blue brightness, 0 to 255.
+ @return 32-bit packed RGB value, which can then be assigned to a
+ variable for later use or passed to the setPixelColor()
+ function. Packed RGB format is predictable, regardless of
+ LED strand color order.
+ */
+ static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) {
+ return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
+ }
+ /*!
+ @brief Convert separate red, green, blue and white values into a
+ single "packed" 32-bit WRGB color.
+ @param r Red brightness, 0 to 255.
+ @param g Green brightness, 0 to 255.
+ @param b Blue brightness, 0 to 255.
+ @param w White brightness, 0 to 255.
+ @return 32-bit packed WRGB value, which can then be assigned to a
+ variable for later use or passed to the setPixelColor()
+ function. Packed WRGB format is predictable, regardless of
+ LED strand color order.
+ */
+ static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
+ return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
+ }
+ static uint32_t ColorHSV(uint16_t hue, uint8_t sat=255, uint8_t val=255);
+ /*!
+ @brief A gamma-correction function for 32-bit packed RGB or WRGB
+ colors. Makes color transitions appear more perceptially
+ correct.
+ @param x 32-bit packed RGB or WRGB color.
+ @return Gamma-adjusted packed color, can then be passed in one of the
+ setPixelColor() functions. Like gamma8(), this uses a fixed
+ gamma correction exponent of 2.6, which seems reasonably okay
+ for average NeoPixels in average tasks. If you need finer
+ control you'll need to provide your own gamma-correction
+ function instead.
+ */
+ static uint32_t gamma32(uint32_t x);
+
+ private:
+ void rp2040Init(uint8_t pin, bool is800KHz) ;
+ void rp2040Show(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz);
+
+ protected:
+
+ bool is800KHz; ///< true if 800 KHz pixels
+ bool begun; ///< true if begin() previously called
+ uint16_t numLEDs; ///< Number of RGB LEDs in strip
+ uint16_t numBytes; ///< Size of 'pixels' buffer below
+ int16_t pin; ///< Output pin number (-1 if not yet set)
+ uint8_t brightness; ///< Strip brightness 0-255 (stored as +1)
+ uint8_t *pixels; ///< Holds LED color values (3 or 4 bytes each)
+ uint8_t rOffset; ///< Red index within each 3- or 4-byte pixel
+ uint8_t gOffset; ///< Index of green byte
+ uint8_t bOffset; ///< Index of blue byte
+ uint8_t wOffset; ///< Index of white (==rOffset if no white)
+ absolute_time_t endTime; ///< Latch timing reference
+ PIO pio; ///< chosen pio for this object
+ uint sm; ///.
+ *
+ */
+
+
+#pragma once
+#include "pico/stdio.h"
+#include "hardware/pio.h"
+#include "pico/time.h"
+#include "ws2812byte.pio.h"
+
+
+
+// The order of primary colors in the NeoPixel data stream can vary among
+// device types, manufacturers and even different revisions of the same
+// item. The third parameter to the Adafruit_NeoPixel constructor encodes
+// the per-pixel byte offsets of the red, green and blue primaries (plus
+// white, if present) in the data stream -- the following #defines provide
+// an easier-to-use named version for each permutation. e.g. NEO_GRB
+// indicates a NeoPixel-compatible device expecting three bytes per pixel,
+// with the first byte transmitted containing the green value, second
+// containing red and third containing blue. The in-memory representation
+// of a chain of NeoPixels is the same as the data-stream order; no
+// re-ordering of bytes is required when issuing data to the chain.
+// Most of these values won't exist in real-world devices, but it's done
+// this way so we're ready for it (also, if using the WS2811 driver IC,
+// one might have their pixels set up in any weird permutation).
+
+// Bits 5,4 of this value are the offset (0-3) from the first byte of a
+// pixel to the location of the red color byte. Bits 3,2 are the green
+// offset and 1,0 are the blue offset. If it is an RGBW-type device
+// (supporting a white primary in addition to R,G,B), bits 7,6 are the
+// offset to the white byte...otherwise, bits 7,6 are set to the same value
+// as 5,4 (red) to indicate an RGB (not RGBW) device.
+// i.e. binary representation:
+// 0bWWRRGGBB for RGBW devices
+// 0bRRRRGGBB for RGB
+
+// RGB NeoPixel permutations; white and red offsets are always same
+// Offset: W R G B
+#define NEO_RGB ((0<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B
+#define NEO_RBG ((0<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G
+#define NEO_GRB ((1<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B
+#define NEO_GBR ((2<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R
+#define NEO_BRG ((1<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G
+#define NEO_BGR ((2<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R
+
+// RGBW NeoPixel permutations; all 4 offsets are distinct
+// Offset: W R G B
+#define NEO_WRGB ((0<<6) | (1<<4) | (2<<2) | (3)) ///< Transmit as W,R,G,B
+#define NEO_WRBG ((0<<6) | (1<<4) | (3<<2) | (2)) ///< Transmit as W,R,B,G
+#define NEO_WGRB ((0<<6) | (2<<4) | (1<<2) | (3)) ///< Transmit as W,G,R,B
+#define NEO_WGBR ((0<<6) | (3<<4) | (1<<2) | (2)) ///< Transmit as W,G,B,R
+#define NEO_WBRG ((0<<6) | (2<<4) | (3<<2) | (1)) ///< Transmit as W,B,R,G
+#define NEO_WBGR ((0<<6) | (3<<4) | (2<<2) | (1)) ///< Transmit as W,B,G,R
+
+#define NEO_RWGB ((1<<6) | (0<<4) | (2<<2) | (3)) ///< Transmit as R,W,G,B
+#define NEO_RWBG ((1<<6) | (0<<4) | (3<<2) | (2)) ///< Transmit as R,W,B,G
+#define NEO_RGWB ((2<<6) | (0<<4) | (1<<2) | (3)) ///< Transmit as R,G,W,B
+#define NEO_RGBW ((3<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B,W
+#define NEO_RBWG ((2<<6) | (0<<4) | (3<<2) | (1)) ///< Transmit as R,B,W,G
+#define NEO_RBGW ((3<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G,W
+
+#define NEO_GWRB ((1<<6) | (2<<4) | (0<<2) | (3)) ///< Transmit as G,W,R,B
+#define NEO_GWBR ((1<<6) | (3<<4) | (0<<2) | (2)) ///< Transmit as G,W,B,R
+#define NEO_GRWB ((2<<6) | (1<<4) | (0<<2) | (3)) ///< Transmit as G,R,W,B
+#define NEO_GRBW ((3<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B,W
+#define NEO_GBWR ((2<<6) | (3<<4) | (0<<2) | (1)) ///< Transmit as G,B,W,R
+#define NEO_GBRW ((3<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R,W
+
+#define NEO_BWRG ((1<<6) | (2<<4) | (3<<2) | (0)) ///< Transmit as B,W,R,G
+#define NEO_BWGR ((1<<6) | (3<<4) | (2<<2) | (0)) ///< Transmit as B,W,G,R
+#define NEO_BRWG ((2<<6) | (1<<4) | (3<<2) | (0)) ///< Transmit as B,R,W,G
+#define NEO_BRGW ((3<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G,W
+#define NEO_BGWR ((2<<6) | (3<<4) | (1<<2) | (0)) ///< Transmit as B,G,W,R
+#define NEO_BGRW ((3<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R,W
+
+// Add NEO_KHZ400 to the color order value to indicate a 400 KHz device.
+// All but the earliest v1 NeoPixels expect an 800 KHz data stream, this is
+// the default if unspecified. Because flash space is very limited on ATtiny
+// devices (e.g. Trinket, Gemma), v1 NeoPixels aren't handled by default on
+// those chips, though it can be enabled by removing the ifndef/endif below,
+// but code will be bigger. Conversely, can disable the NEO_KHZ400 line on
+// other MCUs to remove v1 support and save a little space.
+
+
+#define NEO_KHZ800 0x0000 ///< 800 KHz data transmission
+#define NEO_KHZ400 0x0100 ///< 400 KHz data transmission
+
+typedef uint16_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor
+typedef uint8_t (* pBrightnessFunc)(uint8_t value) ; // pointer to a brigness conversion function
+
+// These two tables are declared outside the Adafruit_NeoPixel class
+// because some boards may require oldschool compilers that don't
+// handle the C++11 constexpr keyword.
+
+/* A PROGMEM (flash mem) table containing 8-bit unsigned sine wave (0-255).
+ Copy & paste this snippet into a Python REPL to regenerate:
+import math
+for x in range(256):
+ print("{:3},".format(int((math.sin(x/128.0*math.pi)+1.0)*127.5+0.5))),
+ if x&15 == 15: print
+*/
+static const uint8_t _NeoPixelSineTable[256] = {
+ 128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173,
+ 176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215,
+ 218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244,
+ 245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255,
+ 255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246,
+ 245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220,
+ 218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179,
+ 176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131,
+ 128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82,
+ 79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40,
+ 37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11,
+ 10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9,
+ 10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35,
+ 37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76,
+ 79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124};
+
+/* Similar to above, but for an 8-bit gamma-correction table.
+ Copy & paste this snippet into a Python REPL to regenerate:
+import math
+gamma=2.6
+for x in range(256):
+ print("{:3},".format(int(math.pow((x)/255.0,gamma)*255.0+0.5))),
+ if x&15 == 15: print
+*/
+static const uint8_t _NeoPixelGammaTable[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
+ 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7,
+ 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12,
+ 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20,
+ 20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29,
+ 30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 75,
+ 76, 77, 78, 80, 81, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96,
+ 97, 99,100,102,103,105,106,108,109,111,112,114,115,117,119,120,
+ 122,124,125,127,129,130,132,134,136,137,139,141,143,145,146,148,
+ 150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,
+ 182,184,186,188,191,193,195,197,199,202,204,206,209,211,213,215,
+ 218,220,223,225,227,230,232,235,237,240,242,245,247,250,252,255};
+
+// gloal hardware resource variables keeping track of configurations an usage on the pi Pico RP2040.
+static int pio0_offset = -1; // offset of loaded pio neopixel program on pio0; -1 if no program loaded
+static int pio1_offset = -1; // offset of loaded pio neopixel program on pio1; -1 if no program loaded
+static int pio_no_sm[2] = {0,0} ; // number of state machines in use for Neopixel
+
+static uint8_t neopixels_gamma8(uint8_t x) {
+ return _NeoPixelGammaTable[x]; // 0-255 in, 0-255 out
+ }
+
+/*!
+ @brief Class that stores state and functions for interacting with
+ Adafruit NeoPixels and compatible devices.
+*/
+class Adafruit_NeoPixel {
+
+ public:
+
+ // Constructor: number of LEDs, pin number, LED type
+ Adafruit_NeoPixel(uint16_t n, uint16_t pin=0,
+ neoPixelType type=NEO_GRB + NEO_KHZ800);
+ Adafruit_NeoPixel(void);
+ ~Adafruit_NeoPixel();
+
+ void begin(void);
+ void show(void);
+ void setBrightnessFunctions(pBrightnessFunc fr, pBrightnessFunc fg, pBrightnessFunc fb, pBrightnessFunc fw);
+ void setPin(uint16_t p);
+ void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b);
+ void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b,
+ uint8_t w);
+ void setPixelColor(uint16_t n, uint32_t c);
+ void fill(uint32_t c=0, uint16_t first=0, uint16_t count=0);
+ void setBrightness(uint8_t);
+ void clear(void);
+ void updateLength(uint16_t n);
+ void updateType(neoPixelType t);
+ /*!
+ @brief Check whether a call to show() will start sending data
+ immediately or will 'block' for a required interval. NeoPixels
+ require a short quiet time (about 300 microseconds) after the
+ last bit is received before the data 'latches' and new data can
+ start being received. Usually one's sketch is implicitly using
+ this time to generate a new frame of animation...but if it
+ finishes very quickly, this function could be used to see if
+ there's some idle time available for some low-priority
+ concurrent task.
+ @return 1 or true if show() will start sending immediately, 0 or false
+ if show() would block (meaning some idle time is available).
+ */
+ bool canShow(void) {
+ int64_t howlongago = absolute_time_diff_us (endTime, get_absolute_time());
+ return (howlongago >= 300L);
+ }
+ /*!
+ @brief Get a pointer directly to the NeoPixel data buffer in RAM.
+ Pixel data is stored in a device-native format (a la the NEO_*
+ constants) and is not translated here. Applications that access
+ this buffer will need to be aware of the specific data format
+ and handle colors appropriately.
+ @return Pointer to NeoPixel buffer (uint8_t* array).
+ @note This is for high-performance applications where calling
+ setPixelColor() on every single pixel would be too slow (e.g.
+ POV or light-painting projects). There is no bounds checking
+ on the array, creating tremendous potential for mayhem if one
+ writes past the ends of the buffer. Great power, great
+ responsibility and all that.
+ */
+ uint8_t *getPixels(void) const { return pixels; };
+ uint8_t getBrightness(void) const;
+ /*!
+ @brief Retrieve the pin number used for NeoPixel data output.
+ @return Arduino pin number (-1 if not set).
+ */
+ int16_t getPin(void) const { return pin; };
+ /*!
+ @brief Return the number of pixels in an Adafruit_NeoPixel strip object.
+ @return Pixel count (0 if not set).
+ */
+ uint16_t numPixels(void) const { return numLEDs; }
+ uint32_t getPixelColor(uint16_t n) const;
+ /*!
+ @brief An 8-bit integer sine wave function, not directly compatible
+ with standard trigonometric units like radians or degrees.
+ @param x Input angle, 0-255; 256 would loop back to zero, completing
+ the circle (equivalent to 360 degrees or 2 pi radians).
+ One can therefore use an unsigned 8-bit variable and simply
+ add or subtract, allowing it to overflow/underflow and it
+ still does the expected contiguous thing.
+ @return Sine result, 0 to 255, or -128 to +127 if type-converted to
+ a signed int8_t, but you'll most likely want unsigned as this
+ output is often used for pixel brightness in animation effects.
+ */
+ static uint8_t sine8(uint8_t x) {
+ return _NeoPixelSineTable[x]; // 0-255 in, 0-255 out
+ }
+ /*!
+ @brief An 8-bit gamma-correction function for basic pixel brightness
+ adjustment. Makes color transitions appear more perceptially
+ correct.
+ @param x Input brightness, 0 (minimum or off/black) to 255 (maximum).
+ @return Gamma-adjusted brightness, can then be passed to one of the
+ setPixelColor() functions. This uses a fixed gamma correction
+ exponent of 2.6, which seems reasonably okay for average
+ NeoPixels in average tasks. If you need finer control you'll
+ need to provide your own gamma-correction function instead.
+ */
+ static uint8_t gamma8(uint8_t x) {
+ return _NeoPixelGammaTable[x]; // 0-255 in, 0-255 out
+ }
+ /*!
+ @brief Convert separate red, green and blue values into a single
+ "packed" 32-bit RGB color.
+ @param r Red brightness, 0 to 255.
+ @param g Green brightness, 0 to 255.
+ @param b Blue brightness, 0 to 255.
+ @return 32-bit packed RGB value, which can then be assigned to a
+ variable for later use or passed to the setPixelColor()
+ function. Packed RGB format is predictable, regardless of
+ LED strand color order.
+ */
+ static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) {
+ return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
+ }
+ /*!
+ @brief Convert separate red, green, blue and white values into a
+ single "packed" 32-bit WRGB color.
+ @param r Red brightness, 0 to 255.
+ @param g Green brightness, 0 to 255.
+ @param b Blue brightness, 0 to 255.
+ @param w White brightness, 0 to 255.
+ @return 32-bit packed WRGB value, which can then be assigned to a
+ variable for later use or passed to the setPixelColor()
+ function. Packed WRGB format is predictable, regardless of
+ LED strand color order.
+ */
+ static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
+ return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
+ }
+ static uint32_t ColorHSV(uint16_t hue, uint8_t sat=255, uint8_t val=255);
+ /*!
+ @brief A gamma-correction function for 32-bit packed RGB or WRGB
+ colors. Makes color transitions appear more perceptially
+ correct.
+ @param x 32-bit packed RGB or WRGB color.
+ @return Gamma-adjusted packed color, can then be passed in one of the
+ setPixelColor() functions. Like gamma8(), this uses a fixed
+ gamma correction exponent of 2.6, which seems reasonably okay
+ for average NeoPixels in average tasks. If you need finer
+ control you'll need to provide your own gamma-correction
+ function instead.
+ */
+ static uint32_t gamma32(uint32_t x);
+
+
+ void rp2040Init(uint8_t pin) ;
+ void rp2040Show(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz);
+ void rp2040changepin(uint8_t set_pin);
+
+ protected:
+
+ bool is800KHz; ///< true if 800 KHz pixels
+ bool begun; ///< true if the state machines & pio has started (after the first show).
+ uint16_t numLEDs; ///< Number of RGB LEDs in strip
+ uint16_t numBytes; ///< Size of 'pixels' buffer below
+ int16_t pin; ///< Output pin number (-1 if not yet set)
+ uint8_t brightness; ///< Strip brightness 0-255 (stored as +1)
+ uint8_t *pixels; ///< Holds LED color values (3 or 4 bytes each)
+ uint8_t *opixels; ///< Hold originally set LED color values ( 3 or 4 bytes each)
+ uint8_t rOffset; ///< Red index within each 3- or 4-byte pixel
+ uint8_t gOffset; ///< Index of green byte
+ uint8_t bOffset; ///< Index of blue byte
+ uint8_t wOffset; ///< Index of white (==rOffset if no white)
+ absolute_time_t endTime; ///< Latch timing reference
+ PIO pio; ///< chosen pio for this object
+ uint sm; ////external/pico_sdk_import.cmake
+
+# This can be dropped into an external project to help locate this SDK
+# It should be include()ed prior to project()
+
+if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
+ set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
+ message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
+ set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
+ message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
+ set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
+ message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
+endif ()
+
+set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
+set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
+set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
+
+if (NOT PICO_SDK_PATH)
+ if (PICO_SDK_FETCH_FROM_GIT)
+ include(FetchContent)
+ set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
+ if (PICO_SDK_FETCH_FROM_GIT_PATH)
+ get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
+ endif ()
+ FetchContent_Declare(
+ pico_sdk
+ GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+ GIT_TAG master
+ )
+ if (NOT pico_sdk)
+ message("Downloading Raspberry Pi Pico SDK")
+ FetchContent_Populate(pico_sdk)
+ set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
+ endif ()
+ set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
+ else ()
+ message(FATAL_ERROR
+ "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
+ )
+ endif ()
+endif ()
+
+get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
+if (NOT EXISTS ${PICO_SDK_PATH})
+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
+endif ()
+
+set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
+if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
+endif ()
+
+set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
+
+include(${PICO_SDK_INIT_CMAKE_FILE})
diff --git a/pico-light-voice/pico_neopixels/ws2812byte.pio b/pico-light-voice/pico_neopixels/ws2812byte.pio
new file mode 100644
index 0000000..02ef00b
--- /dev/null
+++ b/pico-light-voice/pico_neopixels/ws2812byte.pio
@@ -0,0 +1,46 @@
+;
+; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+;
+; SPDX-License-Identifier: BSD-3-Clause
+;
+
+.program ws2812byte
+.side_set 1
+
+.define public T1 2
+.define public T2 5
+.define public T3 3
+
+
+.wrap_target
+bitloop:
+ out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls
+ jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse
+do_one:
+ jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse
+do_zero:
+ nop side 0 [T2 - 1] ; Or drive low, for a short pulse
+.wrap
+
+% c-sdk {
+#include "hardware/clocks.h"
+
+static inline void ws2812byte_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, uint bits) {
+
+ pio_gpio_init(pio, pin);
+ pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
+
+ pio_sm_config c = ws2812byte_program_get_default_config(offset);
+ sm_config_set_sideset_pins(&c, pin);
+ sm_config_set_out_shift(&c, false, true, bits);
+ sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
+
+ int cycles_per_bit = ws2812byte_T1 + ws2812byte_T2 + ws2812byte_T3;
+ float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
+ sm_config_set_clkdiv(&c, div);
+
+ pio_sm_init(pio, sm, offset, &c);
+ pio_sm_set_enabled(pio, sm, true);
+}
+%}
+
diff --git a/pico-light-voice/pico_sdk_import.cmake b/pico-light-voice/pico_sdk_import.cmake
new file mode 100644
index 0000000..f63ee3f
--- /dev/null
+++ b/pico-light-voice/pico_sdk_import.cmake
@@ -0,0 +1,64 @@
+# This is a copy of /external/pico_sdk_import.cmake
+
+# This can be dropped into an external project to help locate this SDK
+# It should be include()ed prior to project()
+
+# todo document
+
+if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
+ set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
+ message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
+ set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
+ message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
+ set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
+ message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
+endif ()
+
+set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the PICO SDK")
+set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO SDK from git if not otherwise locatable")
+set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
+
+if (NOT PICO_SDK_PATH)
+ if (PICO_SDK_FETCH_FROM_GIT)
+ include(FetchContent)
+ set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
+ if (PICO_SDK_FETCH_FROM_GIT_PATH)
+ get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
+ endif ()
+ FetchContent_Declare(
+ pico_sdk
+ GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+ GIT_TAG master
+ )
+ if (NOT pico_sdk)
+ message("Downloading PICO SDK")
+ FetchContent_Populate(pico_sdk)
+ set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
+ endif ()
+ set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
+ else ()
+ message(FATAL_ERROR
+ "PICO SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
+ )
+ endif ()
+endif ()
+
+get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
+if (NOT EXISTS ${PICO_SDK_PATH})
+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
+endif ()
+
+set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
+if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the PICO SDK")
+endif ()
+
+set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the PICO SDK" FORCE)
+
+include(${PICO_SDK_INIT_CMAKE_FILE})
diff --git a/pico-light-voice/source/main.cpp b/pico-light-voice/source/main.cpp
new file mode 100644
index 0000000..2527776
--- /dev/null
+++ b/pico-light-voice/source/main.cpp
@@ -0,0 +1,217 @@
+#include "ei_run_classifier.h"
+#include "Adafruit_NeoPixel.hpp"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// ############ ADC and Model Stuff ############
+
+#define NSAMP 5000
+// set this to determine sample rate
+// 0 = 500,000 Hz
+// 960 = 50,000 Hz
+// 9600 = 5,000 Hz
+#define CLOCK_DIV 9600
+#define CAPTURE_CHANNEL 0
+#define LED_PIN 25
+
+float features[NSAMP];
+uint16_t capture_buf[NSAMP];
+
+// ############ Lights Stuff ############
+#define PIN 7
+
+// ############ Functions ############
+int raw_feature_get_data(size_t offset, size_t length, float *out_ptr) {
+ memcpy(out_ptr, features + offset, length * sizeof(float));
+ return 0;
+}
+
+void core1_entry() {
+ Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN,
+ NEO_GRB + NEO_KHZ800);
+ strip.begin();
+ strip.setBrightness(64);
+ strip.show();
+
+ // tell the other core we're ready for data
+ multicore_fifo_push_blocking(0);
+
+ uint32_t state = 0;
+ bool sent_req = false;
+ uint32_t loop_idx = 0;
+ while (1) {
+ // check for new state information
+ if (multicore_fifo_rvalid()) {
+ uint32_t val = multicore_fifo_pop_blocking();
+
+ // 0 means state unchanged
+ if (val != 0) {
+ state = val;
+ }
+
+ sent_req = false;
+ }
+
+ else if (!sent_req) {
+ // tell the other core we're ready for data
+ multicore_fifo_push_blocking(0);
+ // make sure we don't fill up the other queue
+ sent_req = true;
+ }
+
+ // turn on
+ if (state == 1) {
+ uint16_t i, j;
+
+ for(j=0; j<256; j++) { // 5 cycles of all colors on wheel
+ for(i=0; i< strip.numPixels(); i++) {
+ uint8_t WheelPos = ((i * 256 / strip.numPixels()) + j) & 255;
+ uint32_t c = 0;
+
+ WheelPos = 255 - WheelPos;
+ if(WheelPos < 85) {
+ c = strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
+ }
+ if(WheelPos < 170) {
+ WheelPos -= 85;
+ c = strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
+ }
+ WheelPos -= 170;
+ c = strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
+
+ strip.setPixelColor(i, c);
+ }
+
+ strip.show();
+ sleep_ms(10);
+ }
+ }
+
+ // turn off
+ else if (state == 2) {
+ strip.clear();
+ strip.show();
+ sleep_ms(200);
+ }
+ }
+}
+
+int main()
+{
+ stdio_usb_init();
+ stdio_init_all();
+
+ multicore_launch_core1(core1_entry);
+
+ gpio_init(LED_PIN);
+ gpio_set_dir(LED_PIN, GPIO_OUT);
+
+ ei_impulse_result_t result = {nullptr};
+
+ signal_t features_signal;
+ features_signal.total_length = NSAMP;
+ features_signal.get_data = &raw_feature_get_data;
+
+ if (NSAMP != EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE) {
+ while (1) {
+ printf("Input frame size incorrect!\n");
+ sleep_ms(2000);
+ }
+ }
+
+ adc_gpio_init(26 + CAPTURE_CHANNEL);
+
+ adc_init();
+ adc_select_input(CAPTURE_CHANNEL);
+ adc_fifo_setup(
+ true, // Write conversions to the sample FIFO
+ true, // Enable DMA data request (DREQ)
+ 1, // DREQ (and IRQ) true when >= 1 sample there
+ false, // Disable err bit
+ false // No 8-bit shift
+ );
+
+ // set sample rate
+ adc_set_clkdiv(CLOCK_DIV);
+
+ sleep_ms(1000);
+ // Set up the DMA to start xfer data as soon as it appears in FIFO
+ uint dma_chan = dma_claim_unused_channel(true);
+ dma_channel_config cfg = dma_channel_get_default_config(dma_chan);
+
+ // Reading from constant address, writing to incrementing byte address
+ channel_config_set_transfer_data_size(&cfg, DMA_SIZE_16);
+ channel_config_set_read_increment(&cfg, false);
+ channel_config_set_write_increment(&cfg, true);
+
+ // Pace transfers based on availability of ADC samples
+ channel_config_set_dreq(&cfg, DREQ_ADC);
+
+ while (true) {
+ adc_fifo_drain();
+ adc_run(false);
+
+ dma_channel_configure(dma_chan, &cfg,
+ capture_buf, // dst
+ &adc_hw->fifo, // src
+ NSAMP, // transfer count
+ true // start immediately
+ );
+
+ gpio_put(LED_PIN, 1);
+ adc_run(true);
+
+ // invoke the impulse
+ EI_IMPULSE_ERROR res = run_classifier(&features_signal, &result,
+ false);
+
+ if (res != 0) {
+ printf("ERROR: Edge Impulse Model Returned %d", res);
+ return 1;
+ }
+
+ if (EI_CLASSIFIER_HAS_ANOMALY == 1) printf("Anomaly!\n");
+
+ const float thresh = 0.9;
+
+ uint32_t state = 0;
+ for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
+ if (ix == 0 && result.classification[ix].value > thresh) {
+ printf("GO\n");
+ state = 1;
+ }
+
+ if (ix == 2 && result.classification[ix].value > thresh) {
+ printf("STOP\n");
+ state = 2;
+ }
+ }
+
+ if (multicore_fifo_rvalid()) {
+ multicore_fifo_pop_blocking();
+ multicore_fifo_push_blocking(state);
+ }
+
+ gpio_put(LED_PIN, 0);
+ dma_channel_wait_for_finish_blocking(dma_chan);
+
+ // copy everything to feature buffer to run model
+ // this is probably slow, idk
+ uint64_t sum = 0;
+ for (uint32_t i=0; i/external/pico_sdk_import.cmake
+
+# This can be dropped into an external project to help locate this SDK
+# It should be include()ed prior to project()
+
+# todo document
+
+if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
+ set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
+ message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
+ set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
+ message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
+ set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
+ message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
+endif ()
+
+set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the PICO SDK")
+set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO SDK from git if not otherwise locatable")
+set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
+
+if (NOT PICO_SDK_PATH)
+ if (PICO_SDK_FETCH_FROM_GIT)
+ include(FetchContent)
+ set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
+ if (PICO_SDK_FETCH_FROM_GIT_PATH)
+ get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
+ endif ()
+ FetchContent_Declare(
+ pico_sdk
+ GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+ GIT_TAG master
+ )
+ if (NOT pico_sdk)
+ message("Downloading PICO SDK")
+ FetchContent_Populate(pico_sdk)
+ set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
+ endif ()
+ set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
+ else ()
+ message(FATAL_ERROR
+ "PICO SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
+ )
+ endif ()
+endif ()
+
+get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
+if (NOT EXISTS ${PICO_SDK_PATH})
+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
+endif ()
+
+set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
+if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the PICO SDK")
+endif ()
+
+set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the PICO SDK" FORCE)
+
+include(${PICO_SDK_INIT_CMAKE_FILE})
diff --git a/pico-voice-v1/source/main.cpp b/pico-voice-v1/source/main.cpp
new file mode 100644
index 0000000..983473c
--- /dev/null
+++ b/pico-voice-v1/source/main.cpp
@@ -0,0 +1,143 @@
+#include "ei_run_classifier.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define NSAMP 5000
+// set this to determine sample rate
+// 0 = 500,000 Hz
+// 960 = 50,000 Hz
+// 9600 = 5,000 Hz
+#define CLOCK_DIV 9600
+
+#define CAPTURE_CHANNEL 0
+#define LED_PIN 25
+
+float features[NSAMP];
+uint16_t capture_buf[NSAMP];
+
+int raw_feature_get_data(size_t offset, size_t length, float *out_ptr)
+{
+ memcpy(out_ptr, features + offset, length * sizeof(float));
+ return 0;
+}
+
+int main()
+{
+ stdio_usb_init();
+ stdio_init_all();
+
+ gpio_init(LED_PIN);
+ gpio_set_dir(LED_PIN, GPIO_OUT);
+
+ ei_impulse_result_t result = {nullptr};
+
+ signal_t features_signal;
+ features_signal.total_length = NSAMP;
+ features_signal.get_data = &raw_feature_get_data;
+
+ if (NSAMP != EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE) {
+ while (1) {
+ printf("Input frame size incorrect!\n");
+ sleep_ms(2000);
+ }
+ }
+
+ adc_gpio_init(26 + CAPTURE_CHANNEL);
+
+ adc_init();
+ adc_select_input(CAPTURE_CHANNEL);
+ adc_fifo_setup(
+ true, // Write conversions to the sample FIFO
+ true, // Enable DMA data request (DREQ)
+ 1, // DREQ (and IRQ) true when >= 1 sample there
+ false, // Disable err bit
+ false // No 8-bit shift
+ );
+
+ // set sample rate
+ adc_set_clkdiv(CLOCK_DIV);
+
+ sleep_ms(1000);
+ // Set up the DMA to start xfer data as soon as it appears in FIFO
+ uint dma_chan = dma_claim_unused_channel(true);
+ dma_channel_config cfg = dma_channel_get_default_config(dma_chan);
+
+ // Reading from constant address, writing to incrementing byte address
+ channel_config_set_transfer_data_size(&cfg, DMA_SIZE_16);
+ channel_config_set_read_increment(&cfg, false);
+ channel_config_set_write_increment(&cfg, true);
+
+ // Pace transfers based on availability of ADC samples
+ channel_config_set_dreq(&cfg, DREQ_ADC);
+
+ while (true) {
+ adc_fifo_drain();
+ adc_run(false);
+
+ dma_channel_configure(dma_chan, &cfg,
+ capture_buf, // dst
+ &adc_hw->fifo, // src
+ NSAMP, // transfer count
+ true // start immediately
+ );
+
+ gpio_put(LED_PIN, 1);
+ adc_run(true);
+
+ // invoke the impulse
+ EI_IMPULSE_ERROR res = run_classifier(&features_signal, &result,
+ false);
+
+ if (res != 0) {
+ printf("run_classifier returned: %d\n", res);
+ return 1;
+ }
+
+ // uncomment this for timing information
+ /*
+ printf("DSP: %d ms., Class.: %d ms., Anomaly: %d ms \n",
+ result.timing.dsp, result.timing.classification,
+ result.timing.anomaly);
+ */
+
+ if (EI_CLASSIFIER_HAS_ANOMALY == 1) printf("Anomaly!\n");
+
+
+ const float thresh = 0.9;
+ for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
+ //printf("%.5f", result.classification[ix].value);
+ //if (ix != EI_CLASSIFIER_LABEL_COUNT - 1) printf(", ");
+
+
+ if (ix == 0 && result.classification[ix].value > thresh)
+ printf("GO\n");
+ if (ix == 2 && result.classification[ix].value > thresh)
+ printf("STOP\n");
+
+ }
+
+ //printf("\n");
+
+ gpio_put(LED_PIN, 0);
+ dma_channel_wait_for_finish_blocking(dma_chan);
+
+ // Copy everything to feature buffer to run model. In my training,
+ // I fed the model float values from WAVs so we need to bring the
+ // sample level to zero and convert to floats
+ uint64_t sum = 0;
+ for (uint32_t i=0; i