diff --git a/.gitignore b/.gitignore index d163863..b09316d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -build/ \ No newline at end of file +build/ +edge-impulse-sdk/ +tflite-model/ +model-parameters/ \ No newline at end of file diff --git a/adc_fft/.pico_sdk_import.cmake.icloud b/adc_fft/.pico_sdk_import.cmake.icloud new file mode 100644 index 0000000..2934d8b Binary files /dev/null and b/adc_fft/.pico_sdk_import.cmake.icloud differ diff --git a/adc_time/.pico_sdk_import.cmake.icloud b/adc_time/.pico_sdk_import.cmake.icloud new file mode 100644 index 0000000..2934d8b Binary files /dev/null and b/adc_time/.pico_sdk_import.cmake.icloud differ diff --git a/adc_time/pico_sdk_import.cmake b/adc_time/pico_sdk_import.cmake deleted file mode 100644 index e02a33e..0000000 --- a/adc_time/pico_sdk_import.cmake +++ /dev/null @@ -1,62 +0,0 @@ -# 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() - -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-daq/.DS_Store b/pico-daq/.DS_Store new file mode 100644 index 0000000..8be3f12 Binary files /dev/null and b/pico-daq/.DS_Store differ diff --git a/pico-daq/CMakeLists.txt b/pico-daq/CMakeLists.txt new file mode 100644 index 0000000..56ae42b --- /dev/null +++ b/pico-daq/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.12) + +include(pico_sdk_import.cmake) + +project(pico_daq) + +pico_sdk_init() + +add_executable(pico_daq + pico_daq.cpp + base64.cpp +) + +pico_enable_stdio_usb(pico_daq 1) +pico_enable_stdio_uart(pico_daq 1) + +pico_add_extra_outputs(pico_daq) + +target_link_libraries(pico_daq + pico_stdlib + hardware_adc + hardware_dma + ) diff --git a/pico-daq/README.md b/pico-daq/README.md new file mode 100644 index 0000000..3ceb71e --- /dev/null +++ b/pico-daq/README.md @@ -0,0 +1,19 @@ +# pico-daq + +This program samples from the ADC, converts the sampled values to base64, and then dumps it out over Serial. You can then read in the base 64 values into a program to convert them to whatever you'd like. + +## A note on the sampling + +At 5 kHz w/ 16-bit samples (the default in the program) the Pico cannot write out data as fast as it reads it. Therefore, at the end of each sampling window, the program will pause ADC sampling as it finishes writing out the rest of the samples. This will lead to some jumps in the data. For my purposes of collecting audio for training a ML model this is fine. If this is not ok, try using 8-bit samples or turn down the sample rate until the LED on the Pico starts to flash. + +## Logging Base64 Values + +On macOS and Linux you can use the `screen` tool to save off the base64 values: + + screen -L /dev/tty.usbmodem21301 115200 + +This command opens a serial console for the Pico and then will save the resulting text to the file `screenlog.0`, which you can then convert back to whatever format you'd like. Obviously you'd need to change `/dev/....` to the address of your Pico. + +## Converting to WAV + +I included a sample program to read in the base64 text values and convert it to WAV files that can be played on your computer. See `py/b64_16_to_wav.py`. \ No newline at end of file diff --git a/pico-daq/base64.cpp b/pico-daq/base64.cpp new file mode 100644 index 0000000..d055ee2 --- /dev/null +++ b/pico-daq/base64.cpp @@ -0,0 +1,121 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "base64.h" + +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) + ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + + } + + return ret; + +} +std::string base64_decode(std::string const& encoded_string) { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = base64_chars.find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = base64_chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +} diff --git a/pico-daq/base64.h b/pico-daq/base64.h new file mode 100644 index 0000000..ea4ca55 --- /dev/null +++ b/pico-daq/base64.h @@ -0,0 +1,3 @@ +#include + +std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len); diff --git a/pico-daq/pico_daq.cpp b/pico-daq/pico_daq.cpp new file mode 100644 index 0000000..a6f54fb --- /dev/null +++ b/pico-daq/pico_daq.cpp @@ -0,0 +1,92 @@ +// Sample from the ADC continuously at a particular sample rate +// and then outputs base64 bytes via Serial +// much of this code is from pico-examples/adc/dma_capture/dma_capture.c +// the rest is written by Alex Wulff (www.AlexWulff.com) + +#include +#include +#include "pico/stdlib.h" +#include "hardware/adc.h" +#include "hardware/dma.h" +#include "base64.h" + +// set this to determine sample rate +// 0 = 500,000 Hz +// 960 = 50,000 Hz +// 9600 = 5,000 Hz +#define CLOCK_DIV 9600 + +// Channel 0 is GPIO26 +#define CAPTURE_CHANNEL 0 +#define LED_PIN 25 +#define NSAMP 20000 + +uint16_t capture_buf[NSAMP]; +uint16_t sending_buf[NSAMP]; + +int main() { + stdio_init_all(); + + gpio_init(LED_PIN); + gpio_set_dir(LED_PIN, GPIO_OUT); + adc_gpio_init(26 + CAPTURE_CHANNEL); + + adc_init(); + adc_select_input(CAPTURE_CHANNEL); + adc_fifo_setup( + true, // Write each completed conversion to the sample FIFO + true, // Enable DMA data request (DREQ) + 1, // DREQ (and IRQ) asserted when at least 1 sample present + false, // We won't see the ERR bit because of 8 bit reads; disable. + false // Don't shift each sample to 8 bits when pushing to FIFO + ); + + // set sample rate + adc_set_clkdiv(CLOCK_DIV); + + sleep_ms(1000); + // Set up the DMA to start transferring 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 addresses + 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 (1) { + 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 + ); + + // if the light does not flash, then there is some data loss. + // pico can't print the data out fast enough, so the capturing + // finishes before the data is done printing. This will result + // in the ADC not collecting for portions of the sampling + + // run sampling routine + gpio_put(LED_PIN, 1); + adc_run(true); + + // first transmission will be garbage since we haven't filled the buffer yet + std::string encoded = + base64_encode((unsigned char const *)sending_buf, NSAMP*2); + printf("%s", encoded.c_str()); + + gpio_put(LED_PIN, 0); + + dma_channel_wait_for_finish_blocking(dma_chan); + + + memcpy(sending_buf, capture_buf, NSAMP*2); + } +} diff --git a/adc_fft/pico_sdk_import.cmake b/pico-daq/pico_sdk_import.cmake similarity index 100% rename from adc_fft/pico_sdk_import.cmake rename to pico-daq/pico_sdk_import.cmake diff --git a/pico-daq/py/b64_16_to_wave.py b/pico-daq/py/b64_16_to_wave.py new file mode 100644 index 0000000..aa5806a --- /dev/null +++ b/pico-daq/py/b64_16_to_wave.py @@ -0,0 +1,35 @@ +from pydub import AudioSegment +import numpy as np +import base64 + +if __name__=="__main__": + infile = "/Users/alex/Desktop/pico/other-3" + outfile = "/Users/alex/Desktop/pico/other.wav" + + f = open(infile, "r") + + byte_data = bytearray() + vals = f.read(6000) + # Reading all at once didn't work for some reason + while vals: + byte_data.extend(base64.b64decode(vals)) + vals = f.read(6000) + f.close() + + # Enforce little endian + dt = np.dtype(np.uint16) + dt = dt.newbyteorder('<') + data = np.frombuffer(byte_data, dtype=dt) + + data = data.astype(np.int32) + data = data-np.mean(data) + data = data.astype(np.int16) + + audio = AudioSegment( + data.tobytes(), + sample_width=2, + frame_rate=5000, + channels=1 + ) + + audio.export(outfile, format="wav") diff --git a/pico-light-voice b/pico-light-voice new file mode 160000 index 0000000..5a82588 --- /dev/null +++ b/pico-light-voice @@ -0,0 +1 @@ +Subproject commit 5a825884753207656080b091d87490348452670c diff --git a/pico-voice-v1 b/pico-voice-v1 new file mode 160000 index 0000000..5a82588 --- /dev/null +++ b/pico-voice-v1 @@ -0,0 +1 @@ +Subproject commit 5a825884753207656080b091d87490348452670c