diff --git a/README.md b/README.md index a4c92ad46..c74a4c266 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ App|Description ### Pico W Networking -These eaxmples are for the Pico W, and are only available for `PICO_BOARD=pico_w` +These examples are for the Pico W, and are only available for `PICO_BOARD=pico_w` App|Description ---|--- diff --git a/pico_w/wifi_vsys/CMakeLists.txt b/pico_w/wifi_vsys/CMakeLists.txt new file mode 100644 index 000000000..9e81b1343 --- /dev/null +++ b/pico_w/wifi_vsys/CMakeLists.txt @@ -0,0 +1,53 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) +set(PICO_SDK_PATH "/home/username/pico/pico-sdk") +set(PICO_EXTRAS_PATH "/home/username/pico/pico-extras") + +set(PICO_BOARD pico_w CACHE STRING "Board type") + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) +include(${PICO_EXTRAS_PATH}/external/pico_extras_import.cmake) + + +if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0") + message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") +endif() + +project(wifi_vsys C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +add_executable(wifi_vsys wifi_vsys.cpp ) + +pico_set_program_name(wifi_vsys "wifi_vsys") +pico_set_program_version(wifi_vsys "0.1") + +pico_enable_stdio_uart(wifi_vsys 0) +pico_enable_stdio_usb(wifi_vsys 1) + +# Add the standard library to the build +target_link_libraries(wifi_vsys + pico_stdlib + pico_lwip_http + pico_cyw43_arch_lwip_threadsafe_background + hardware_adc + ) + +# Add the standard include files to the build +target_include_directories(wifi_vsys PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required +) + +pico_add_extra_outputs(wifi_vsys) diff --git a/pico_w/wifi_vsys/lwipopts.h b/pico_w/wifi_vsys/lwipopts.h new file mode 100644 index 000000000..8571ed509 --- /dev/null +++ b/pico_w/wifi_vsys/lwipopts.h @@ -0,0 +1,10 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +// Generally you would define your own explicit list of lwIP options +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html) +// +// This example uses a common include to avoid repetition +#include "lwipopts_examples_common.h" + +#endif diff --git a/pico_w/wifi_vsys/wifi_vsys.cpp b/pico_w/wifi_vsys/wifi_vsys.cpp new file mode 100644 index 000000000..14d586d49 --- /dev/null +++ b/pico_w/wifi_vsys/wifi_vsys.cpp @@ -0,0 +1,158 @@ +#include +#include "pico/stdlib.h" +#include "pico/cyw43_arch.h" +#include "hardware/gpio.h" +#include "hardware/adc.h" //to read ADC for soil moisture sensor, connected to pin 36 (3.3V), pin 33 (GND/AGND), pin 34 (GPIO28/ADC2) +#include "lwip/apps/http_client.h" //http client +#include "pico/cyw43_arch/arch_threadsafe_background.h" //to get access to void cyw43_thread_enter(void); and void cyw43_thread_exit(void); + +#ifndef CYW43_WL_GPIO2 +#define CYW43_WL_GPIO2 2 //this is not defined in pico_w.h +#endif + +const float conversion_factor = 3.3f / (1 << 12); //ADC_VREF=3.3, so this is the ADC resolution. +const char ssid[] = "myWifiNetwork"; +const char pass[] = "myWifiPassword"; +const uint32_t auth = CYW43_AUTH_WPA2_MIXED_PSK; + +//Http client variables +bool httpBusy = false; +char httpBuff[1000]; //if not big enough to read the result or headers, pico will hang. +httpc_connection_t settings; +ip_addr_t ip; +char apicall[64]; + + +//handler for HTTP client result +void httpresult(void *arg, httpc_result_t httpc_result, u32_t rx_content_len, u32_t srv_res, err_t err){ + printf("[1] local result=%d http result=%d\n", httpc_result, srv_res); +} + +//handler for HTTP client headers +err_t headers(httpc_state_t *connection, void *arg, struct pbuf *hdr, u16_t hdr_len, u32_t content_len){ + pbuf_copy_partial(hdr, httpBuff, hdr->tot_len, 0); + printf("[1] content length=%d, header length %d, headers: %s\n", content_len, hdr_len, httpBuff); + //free memory + pbuf_free(hdr); + return ERR_OK; +} + +//handler for HTTP client body +err_t body(void *arg, struct altcp_pcb *conn, struct pbuf *p, err_t err){ + pbuf_copy_partial(p, httpBuff, p->tot_len, 0); + printf("[1] body: %s", httpBuff); + httpBusy = false; + //free memory + pbuf_free(p); + return ERR_OK; +} + +//Set GPIO29 back to settings for wifi usage (shared pin with ADC3, so settings are changed when activating ADC3) +void SetGPIO29WifiStatus(){ + gpio_set_function(29, GPIO_FUNC_PIO1); //7 + gpio_pull_down(29); + gpio_set_slew_rate(29, GPIO_SLEW_RATE_FAST); //1 + gpio_set_drive_strength(29, GPIO_DRIVE_STRENGTH_12MA); //3 +} + +int main() { + stdio_init_all(); + adc_init(); + adc_gpio_init(28); + + //Initialize wifi + if (cyw43_arch_init()) { + printf("WiFi init failed.\n"); + return 1; + } + cyw43_arch_enable_sta_mode(); + //connect to access point + if (cyw43_arch_wifi_connect_timeout_ms(ssid, pass, auth, 30000)) { + printf("failed to connect.\n"); + return 1; + } else { + printf("Connected.\n"); + } + + //set http client handlers + settings.result_fn = httpresult; + settings.headers_done_fn = headers; + //define IP address that hosts the web api to send the data + IP4_ADDR(&ip, 192, 168, 0, 1); + int ledStatus = 0; + float vsys=0; + + while(true) { + //flip onboard led + ledStatus = 1 - ledStatus; + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, ledStatus); + + //Select ADC2 to read moisture sensor + adc_select_input(2); + uint16_t result = adc_read(); + float voltage = result * conversion_factor; + printf("ADC2 value: 0x%03x, %i, voltage: %3.1f V\n", result, result, voltage); + + //make sure there is not HTTP request in progress or other wifi activity when we want to check the battery status + if (!httpBusy){ + httpBusy = true; + + //prevent wifi background processing so we can use the shared pins + cyw43_thread_enter(); //or macro: CYW43_THREAD_ENTER + + //WL_GPIO2, IP VBUS sense - high if VBUS is present, else low + uint vbus = cyw43_arch_gpio_get(CYW43_WL_GPIO2); + + //we could decide to only check the voltage if not powered by USB port/VBUS and perhaps not check it every cycle. + //if (vbus==0) { + + //initialize and select ADC3/GPIO29 + adc_gpio_init(29); + adc_select_input(3); + + //Read ADC3, result is 1/3 of VSYS, so we still need to multiply the conversion factor with 3 to get the input voltage + uint16_t adc3 = adc_read(); + vsys = adc3 * conversion_factor * 3; + printf("ADC3 value: 0x%03x, %i, voltage: %f V\n", adc3, adc3, vsys); + //set GPIO29 back to the settings for wifi, otherwise cyw43 will not work correctly + SetGPIO29WifiStatus(); + //} + //else{ + //USB/VSYS powered, do nothing (voltage should be somewhere around 5V) + //} + //exit to allow wifi communication again + cyw43_thread_exit(); //or macro: CYW43_THREAD_EXIT + + sprintf(apicall, "/api/echo?m=%3.3f&v=%3.3f&b=%i", voltage, vsys, vbus); + printf("Calling API: %s\n", apicall); + err_t err = httpc_get_file( + &ip, //IP address hosting the API + 5131, //port + apicall, //api call + &settings, //handlers for result/headers + body, //handler for body + NULL, + NULL + ); + printf("[1] http status %d \n", err); + } + else{ + printf("[1] http client is busy...\n"); + } + +#if PICO_CYW43_ARCH_POLL + // if you are using pico_cyw43_arch_poll, then you must poll periodically from your + // main loop (not from a timer) to check for WiFi driver or lwIP work that needs to be done. + cyw43_arch_poll(); + sleep_ms(1); +#else + // if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work + // is done via interrupt in the background. This sleep is just an example of some (blocking) + // work you might be doing. + sleep_ms(5000); +#endif + } + + cyw43_arch_deinit(); + return 0; +} diff --git a/pio/ws2812/ws2812_parallel.c b/pio/ws2812/ws2812_parallel.c index 9fa03c5ba..4b5ae9eac 100644 --- a/pio/ws2812/ws2812_parallel.c +++ b/pio/ws2812/ws2812_parallel.c @@ -20,15 +20,15 @@ #define WS2812_PIN_BASE 2 // horrible temporary hack to avoid changing pattern code -static uint8_t *current_string_out; -static bool current_string_4color; +static uint8_t *current_strip_out; +static bool current_strip_4color; static inline void put_pixel(uint32_t pixel_grb) { - *current_string_out++ = pixel_grb & 0xffu; - *current_string_out++ = (pixel_grb >> 8u) & 0xffu; - *current_string_out++ = (pixel_grb >> 16u) & 0xffu; - if (current_string_4color) { - *current_string_out++ = 0; // todo adjust? + *current_strip_out++ = pixel_grb & 0xffu; + *current_strip_out++ = (pixel_grb >> 8u) & 0xffu; + *current_strip_out++ = (pixel_grb >> 16u) & 0xffu; + if (current_strip_4color) { + *current_strip_out++ = 0; // todo adjust? } } @@ -83,9 +83,9 @@ void pattern_solid(uint len, uint t) { } } -int level = 8; void pattern_fade(uint len, uint t) { + const int level = 8; uint shift = 4; uint max = 16; // let's not draw too much current! @@ -95,7 +95,7 @@ void pattern_fade(uint len, uint t) { slow_t = level; slow_t %= max; - static int error; + static int error = 0; slow_t += error; error = slow_t & ((1u << shift) - 1); slow_t >>= shift; @@ -121,7 +121,7 @@ const struct { #define VALUE_PLANE_COUNT (8 + FRAC_BITS) // we store value (8 bits + fractional bits of a single color (R/G/B/W) value) for multiple -// strings, in bit planes. bit plane N has the Nth bit of each string. +// strips of pixels, in bit planes. bit plane N has the Nth bit of each strip of pixels. typedef struct { // stored MSB first uint32_t planes[VALUE_PLANE_COUNT]; @@ -149,17 +149,17 @@ typedef struct { uint8_t *data; uint data_len; uint frac_brightness; // 256 = *1.0; -} string_t; +} strip_t; // takes 8 bit color values, multiply by brightness and store in bit planes -void transform_strings(string_t **strings, uint num_strings, value_bits_t *values, uint value_length, +void transform_strips(strip_t **strips, uint num_strips, value_bits_t *values, uint value_length, uint frac_brightness) { for (uint v = 0; v < value_length; v++) { memset(&values[v], 0, sizeof(values[v])); - for (int i = 0; i < num_strings; i++) { - if (v < strings[i]->data_len) { + for (int i = 0; i < num_strips; i++) { + if (v < strips[i]->data_len) { // todo clamp? - uint32_t value = (strings[i]->data[v] * strings[i]->frac_brightness) >> 8u; + uint32_t value = (strips[i]->data[v] * strips[i]->frac_brightness) >> 8u; value = (value * frac_brightness) >> 8u; for (int j = 0; j < VALUE_PLANE_COUNT && value; j++, value >>= 1u) { if (value & 1u) values[v].planes[VALUE_PLANE_COUNT - 1 - j] |= 1u << i; @@ -177,29 +177,29 @@ void dither_values(const value_bits_t *colors, value_bits_t *state, const value_ // requested colors * 4 to allow for RGBW static value_bits_t colors[NUM_PIXELS * 4]; -// double buffer the state of the string, since we update next version in parallel with DMAing out old version +// double buffer the state of the pixel strip, since we update next version in parallel with DMAing out old version static value_bits_t states[2][NUM_PIXELS * 4]; -// example - string 0 is RGB only -static uint8_t string0_data[NUM_PIXELS * 3]; -// example - string 1 is RGBW -static uint8_t string1_data[NUM_PIXELS * 4]; +// example - strip 0 is RGB only +static uint8_t strip0_data[NUM_PIXELS * 3]; +// example - strip 1 is RGBW +static uint8_t strip1_data[NUM_PIXELS * 4]; -string_t string0 = { - .data = string0_data, - .data_len = sizeof(string0_data), +strip_t strip0 = { + .data = strip0_data, + .data_len = sizeof(strip0_data), .frac_brightness = 0x40, }; -string_t string1 = { - .data = string1_data, - .data_len = sizeof(string1_data), +strip_t strip1 = { + .data = strip1_data, + .data_len = sizeof(strip1_data), .frac_brightness = 0x100, }; -string_t *strings[] = { - &string0, - &string1, +strip_t *strips[] = { + &strip0, + &strip1, }; // bit plane content dma channel @@ -266,7 +266,7 @@ void dma_init(PIO pio, uint sm) { irq_set_enabled(DMA_IRQ_0, true); } -void output_strings_dma(value_bits_t *bits, uint value_length) { +void output_strips_dma(value_bits_t *bits, uint value_length) { for (uint i = 0; i < value_length; i++) { fragment_start[i] = (uintptr_t) bits[i].planes; // MSB first } @@ -285,7 +285,7 @@ int main() { int sm = 0; uint offset = pio_add_program(pio, &ws2812_parallel_program); - ws2812_parallel_program_init(pio, sm, offset, WS2812_PIN_BASE, count_of(strings), 800000); + ws2812_parallel_program_init(pio, sm, offset, WS2812_PIN_BASE, count_of(strips), 800000); sem_init(&reset_delay_complete_sem, 1, 1); // initially posted so we don't block first time dma_init(pio, sm); @@ -299,17 +299,17 @@ int main() { int brightness = 0; uint current = 0; for (int i = 0; i < 1000; ++i) { - current_string_out = string0.data; - current_string_4color = false; + current_strip_out = strip0.data; + current_strip_4color = false; pattern_table[pat].pat(NUM_PIXELS, t); - current_string_out = string1.data; - current_string_4color = true; + current_strip_out = strip1.data; + current_strip_4color = true; pattern_table[pat].pat(NUM_PIXELS, t); - transform_strings(strings, count_of(strings), colors, NUM_PIXELS * 4, brightness); + transform_strips(strips, count_of(strips), colors, NUM_PIXELS * 4, brightness); dither_values(colors, states[current], states[current ^ 1], NUM_PIXELS * 4); sem_acquire_blocking(&reset_delay_complete_sem); - output_strings_dma(states[current], NUM_PIXELS * 4); + output_strips_dma(states[current], NUM_PIXELS * 4); current ^= 1; t += dir;