From e8b56b414205ac01f8739e8e8ab58b067a39bed3 Mon Sep 17 00:00:00 2001 From: Simon Oberzier Date: Fri, 20 Feb 2026 20:39:17 +0100 Subject: [PATCH] Initial commit --- .gitignore | 3 + CMakeLists.txt | 54 ++++++++++++++++ ble_server.c | 142 ++++++++++++++++++++++++++++++++++++++++++ ble_server.h | 20 ++++++ btstack_config.h | 58 +++++++++++++++++ foo.gatt | 8 +++ gps_handler.c | 54 ++++++++++++++++ gps_handler.h | 23 +++++++ mpu_handler.c | 79 +++++++++++++++++++++++ mpu_handler.h | 25 ++++++++ pico_sdk_import.cmake | 73 ++++++++++++++++++++++ server.c | 52 ++++++++++++++++ 12 files changed, 591 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 ble_server.c create mode 100644 ble_server.h create mode 100644 btstack_config.h create mode 100644 foo.gatt create mode 100644 gps_handler.c create mode 100644 gps_handler.h create mode 100644 mpu_handler.c create mode 100644 mpu_handler.h create mode 100644 pico_sdk_import.cmake create mode 100644 server.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a153af7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +.DS_Store +.idea/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f5a1320 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.12) + +set(PICO_BOARD pico2_w) + +include(pico_sdk_import.cmake) + +project(pico_examples C CXX ASM) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) + +if (PICO_SDK_VERSION_STRING VERSION_LESS "2.2.0") + message(FATAL_ERROR "Raspberry Pi Pico SDK version 2.2.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") +endif() + +pico_sdk_init() + +add_compile_options(-Wall + -Wno-format + -Wno-unused-function +) +if (CMAKE_C_COMPILER_ID STREQUAL "GNU") + add_compile_options(-Wno-maybe-uninitialized) +endif() + +set(BTSTACK_ROOT ${PICO_SDK_PATH}/lib/btstack) + +add_executable(foo + server.c + ble_server.c + gps_handler.c + mpu_handler.c + mpu_handler.h +) + +target_include_directories(foo PUBLIC + ${CMAKE_CURRENT_LIST_DIR} +) +target_link_libraries(foo + pico_stdlib + pico_btstack_ble + pico_btstack_cyw43 + pico_cyw43_arch_none + hardware_adc + hardware_uart + hardware_i2c +) + +pico_enable_stdio_usb(foo 1) +pico_enable_stdio_uart(foo 0) + +pico_btstack_make_gatt_header(foo PRIVATE "${CMAKE_CURRENT_LIST_DIR}/foo.gatt") + +pico_add_extra_outputs(foo) diff --git a/ble_server.c b/ble_server.c new file mode 100644 index 0000000..ab34a65 --- /dev/null +++ b/ble_server.c @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#include "btstack.h" +#include "pico/cyw43_arch.h" +#include "pico/btstack_cyw43.h" +#include "pico/stdlib.h" +#include "foo.h" +#include "ble_server.h" + +#define ATT_CHARACTERISTIC_GENERIC_STRING_VALUE_HANDLE 0x0009 +#define ATT_CHARACTERISTIC_GENERIC_STRING_CLIENT_CONFIGURATION_HANDLE 0x000a + +#define HEARTBEAT_PERIOD_MS 1000 +#define APP_AD_FLAGS 0x06 + +#define GENERIC_MESSAGE_MAX_LEN 64 + +static uint8_t adv_data[] = { + 0x02, BLUETOOTH_DATA_TYPE_FLAGS, APP_AD_FLAGS, + 0x17, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, 'P', 'i', 'c', 'o', ' ', '0', '0', ':', '0', '0', ':', '0', '0', ':', '0', '0', ':', '0', '0', ':', '0', '0', + 0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, 0x1a, 0x18, +}; +static const uint8_t adv_data_len = sizeof(adv_data); + +static int le_generic_string_notification_enabled; +static hci_con_handle_t con_handle; +static char generic_message_buffer[GENERIC_MESSAGE_MAX_LEN]; +static btstack_timer_source_t heartbeat; +static btstack_packet_callback_registration_t hci_event_callback_registration; + +extern uint8_t const profile_data[]; + +static void ble_start_advertising(void) { + uint16_t adv_int_min = 800; + uint16_t adv_int_max = 800; + uint8_t adv_type = 0; + bd_addr_t null_addr; + memset(null_addr, 0, 6); + gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00); + assert(adv_data_len <= 31); + + bd_addr_t local_addr; + gap_local_bd_addr(local_addr); + sprintf((char*)&adv_data[12], "%02X:%02X:%02X:%02X:%02X:%02X", + local_addr[0], local_addr[1], local_addr[2], + local_addr[3], local_addr[4], local_addr[5]); + + gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data); + gap_advertisements_enable(1); +} + + +static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { + UNUSED(size); + UNUSED(channel); + if (packet_type != HCI_EVENT_PACKET) return; + + uint8_t event_type = hci_event_packet_get_type(packet); + switch(event_type){ + case BTSTACK_EVENT_STATE: + if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) return; + ble_start_advertising(); + break; + case HCI_EVENT_DISCONNECTION_COMPLETE: + le_generic_string_notification_enabled = 0; + break; + case ATT_EVENT_CAN_SEND_NOW: + if (le_generic_string_notification_enabled) { + att_server_notify(con_handle, ATT_CHARACTERISTIC_GENERIC_STRING_VALUE_HANDLE, (uint8_t*)generic_message_buffer, strlen(generic_message_buffer)); + } + break; + default: + break; + } +} + +static uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size) { + UNUSED(connection_handle); + + if (att_handle == ATT_CHARACTERISTIC_GENERIC_STRING_VALUE_HANDLE) { + return att_read_callback_handle_blob((const uint8_t *)&generic_message_buffer, strlen(generic_message_buffer), offset, buffer, buffer_size); + } + return 0; +} + +static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { + UNUSED(transaction_mode); + UNUSED(offset); + UNUSED(buffer_size); + + if (att_handle == ATT_CHARACTERISTIC_GENERIC_STRING_CLIENT_CONFIGURATION_HANDLE) { + le_generic_string_notification_enabled = little_endian_read_16(buffer, 0) == GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION; + con_handle = connection_handle; + if (le_generic_string_notification_enabled) { + att_server_request_can_send_now_event(con_handle); + } + return 0; + } + return 0; +} + +static void heartbeat_handler(struct btstack_timer_source *ts) { + btstack_run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS); + btstack_run_loop_add_timer(ts); +} + +void start_server(void) { + if (cyw43_arch_init()) { + return; + } + + l2cap_init(); + sm_init(); + + att_server_init(profile_data, att_read_callback, att_write_callback); + + hci_event_callback_registration.callback = &packet_handler; + hci_add_event_handler(&hci_event_callback_registration); + + att_server_register_packet_handler(packet_handler); + + heartbeat.process = &heartbeat_handler; + btstack_run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS); + btstack_run_loop_add_timer(&heartbeat); + + hci_power_control(HCI_POWER_ON); +} + +void stop_server(void) { + hci_power_control(HCI_POWER_OFF); + btstack_run_loop_remove_timer(&heartbeat); + cyw43_arch_deinit(); +} + +void send_message(const char* message) { + snprintf(generic_message_buffer, GENERIC_MESSAGE_MAX_LEN, "%s", message); + if (le_generic_string_notification_enabled) { + att_server_request_can_send_now_event(con_handle); + } +} diff --git a/ble_server.h b/ble_server.h new file mode 100644 index 0000000..b7dd1a0 --- /dev/null +++ b/ble_server.h @@ -0,0 +1,20 @@ +#ifndef BLE_SERVER_H +#define BLE_SERVER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void start_server(void); + +void stop_server(void); + +void send_message(const char* message); + +#ifdef __cplusplus +} +#endif + +#endif // BLE_SERVER_H diff --git a/btstack_config.h b/btstack_config.h new file mode 100644 index 0000000..0eacc70 --- /dev/null +++ b/btstack_config.h @@ -0,0 +1,58 @@ +#ifndef _PICO_BTSTACK_CONFIG__H +#define _PICO_BTSTACK_CONFIG__H + +#ifndef ENABLE_BLE +#error Please link to pico_btstack_ble +#endif + +// BTstack features that can be enabled +#define ENABLE_LE_PERIPHERAL +#define ENABLE_LOG_INFO +#define ENABLE_LOG_ERROR +#define ENABLE_PRINTF_HEXDUMP + +// for the client +#if RUNNING_AS_CLIENT +#define ENABLE_LE_CENTRAL +#define MAX_NR_GATT_CLIENTS 1 +#else +#define MAX_NR_GATT_CLIENTS 0 +#endif + +// BTstack configuration. buffers, sizes, ... +#define HCI_OUTGOING_PRE_BUFFER_SIZE 4 +#define HCI_ACL_PAYLOAD_SIZE (255 + 4) +#define HCI_ACL_CHUNK_SIZE_ALIGNMENT 4 +#define MAX_NR_HCI_CONNECTIONS 1 +#define MAX_NR_SM_LOOKUP_ENTRIES 3 +#define MAX_NR_WHITELIST_ENTRIES 16 +#define MAX_NR_LE_DEVICE_DB_ENTRIES 16 + +// Limit number of ACL/SCO Buffer to use by stack to avoid cyw43 shared bus overrun +#define MAX_NR_CONTROLLER_ACL_BUFFERS 3 +#define MAX_NR_CONTROLLER_SCO_PACKETS 3 + +// Enable and configure HCI Controller to Host Flow Control to avoid cyw43 shared bus overrun +#define ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL +#define HCI_HOST_ACL_PACKET_LEN (255+4) +#define HCI_HOST_ACL_PACKET_NUM 3 +#define HCI_HOST_SCO_PACKET_LEN 120 +#define HCI_HOST_SCO_PACKET_NUM 3 + +// Link Key DB and LE Device DB using TLV on top of Flash Sector interface +#define NVM_NUM_DEVICE_DB_ENTRIES 16 +#define NVM_NUM_LINK_KEYS 16 + +// We don't give btstack a malloc, so use a fixed-size ATT DB. +#define MAX_ATT_DB_SIZE 512 + +// BTstack HAL configuration +#define HAVE_EMBEDDED_TIME_MS +// map btstack_assert onto Pico SDK assert() +#define HAVE_ASSERT +// Some USB dongles take longer to respond to HCI reset (e.g. BCM20702A). +#define HCI_RESET_RESEND_TIMEOUT_MS 1000 +#define ENABLE_SOFTWARE_AES128 +#define ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS + +#endif // MICROPY_INCLUDED_EXTMOD_BTSTACK_BTSTACK_CONFIG_H diff --git a/foo.gatt b/foo.gatt new file mode 100644 index 0000000..e12f776 --- /dev/null +++ b/foo.gatt @@ -0,0 +1,8 @@ +PRIMARY_SERVICE, GAP_SERVICE +CHARACTERISTIC, GAP_DEVICE_NAME, READ, "Pico 2 ble example" + +PRIMARY_SERVICE, GATT_SERVICE +CHARACTERISTIC, GATT_DATABASE_HASH, READ, + +PRIMARY_SERVICE, 0000FF00-0000-1000-8000-00805F9B34FB, GenericStringService +CHARACTERISTIC, 0000FF01-0000-1000-8000-00805F9B34FB, READ | NOTIFY | DYNAMIC, GenericStringCharacteristic diff --git a/gps_handler.c b/gps_handler.c new file mode 100644 index 0000000..3dd99b5 --- /dev/null +++ b/gps_handler.c @@ -0,0 +1,54 @@ +#include "gps_handler.h" +#include +#include + +static char buffer[BUFFER_SIZE]; +static int gps_index = 0; + +static float convert_to_decimal(char *coord, char dir) { + float raw = atof(coord); + int degrees = (int)(raw / 100); + float minutes = raw - (degrees * 100); + float decimal = degrees + (minutes / 60.0f); + if (dir == 'S' || dir == 'W') decimal *= -1.0f; + return decimal; +} + +void gps_init(void) { + uart_init(UART_ID, BAUD_RATE); + gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART); + gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART); + + uart_set_hw_flow(UART_ID, false, false); + uart_set_format(UART_ID, 8, 1, UART_PARITY_NONE); +} + +void gps_update(GPS_Data_t *data) { + while (uart_is_readable(UART_ID)) { + char c = uart_getc(UART_ID); + if (c == '\n') { + buffer[gps_index] = '\0'; + if (strstr(buffer, "$GPRMC")) { + char *token = strtok(buffer, ","); + int field = 0; + char lat[16] = {0}, lon[16] = {0}, lat_dir = 0, lon_dir = 0; + while (token != NULL) { + field++; + if (field == 4) strcpy(lat, token); + if (field == 5) lat_dir = token[0]; + if (field == 6) strcpy(lon, token); + if (field == 7) lon_dir = token[0]; + token = strtok(NULL, ","); + } + if (lat[0] != 0 && lon[0] != 0) { + data->latitude = convert_to_decimal(lat, lat_dir); + data->longitude = convert_to_decimal(lon, lon_dir); + data->has_fix = true; + } + } + gps_index = 0; + } else if (gps_index < BUFFER_SIZE - 1) { + buffer[gps_index++] = c; + } + } +} \ No newline at end of file diff --git a/gps_handler.h b/gps_handler.h new file mode 100644 index 0000000..b8d05a7 --- /dev/null +++ b/gps_handler.h @@ -0,0 +1,23 @@ +#ifndef GPS_HANDLER_H +#define GPS_HANDLER_H + +#include "pico/stdlib.h" +#include "hardware/uart.h" + +#define UART_ID uart1 +#define BAUD_RATE 9600 +#define UART_TX_PIN 8 +#define UART_RX_PIN 9 +#define BUFFER_SIZE 128 + +typedef struct { + float latitude; + float longitude; + bool has_fix; +} GPS_Data_t; + +void gps_init(void); + +void gps_update(GPS_Data_t *data); + +#endif //GPS_HANDLER_H \ No newline at end of file diff --git a/mpu_handler.c b/mpu_handler.c new file mode 100644 index 0000000..a35761a --- /dev/null +++ b/mpu_handler.c @@ -0,0 +1,79 @@ +#include "mpu_handler.h" +#include + +static float offset_ax = 0, offset_ay = 0, offset_az = 0; + +static void mpu_write(uint8_t addr, uint8_t reg, uint8_t data) { + uint8_t buf[2] = {reg, data}; + i2c_write_blocking(I2C_PORT, addr, buf, 2, false); +} + +static void mpu_read(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t len) { + i2c_write_blocking(I2C_PORT, addr, ®, 1, true); + i2c_read_timeout_us(I2C_PORT, addr, buf, len, false, 2500); +} + +void mpu_init(void) { + i2c_init(I2C_PORT, 400 * 1000); + gpio_set_function(SDA_PIN, GPIO_FUNC_I2C); + gpio_set_function(SCL_PIN, GPIO_FUNC_I2C); + gpio_pull_up(SDA_PIN); + gpio_pull_up(SCL_PIN); + + // Wake MPU-92.65 + mpu_write(MPU_ADDR, 0x6B, 0x00); + sleep_ms(100); + + // Enable Bypass Mode to see the Magnetometer (AK8963) + mpu_write(MPU_ADDR, 0x37, 0x02); + sleep_ms(10); + + // Initialize Magnetometer: Power down then Continuous measurement mode 2 (100Hz) + mpu_write(MAG_ADDR, 0x0A, 0x00); + sleep_ms(10); + mpu_write(MAG_ADDR, 0x0A, 0x16); + sleep_ms(10); +} + +void mpu_calibrate(void) { + int num_samples = 20; + int32_t sum_ax = 0, sum_ay = 0, sum_az = 0; + uint8_t raw[6]; + + for (int i = 0; i < num_samples; i++) { + mpu_read(MPU_ADDR, 0x3B, raw, 6); + sum_ax += (int16_t)((raw[0] << 8) | raw[1]); + sum_ay += (int16_t)((raw[2] << 8) | raw[3]); + sum_az += (int16_t)((raw[4] << 8) | raw[5]); + sleep_ms(10); + } + + offset_ax = (float)sum_ax / num_samples; + offset_ay = (float)sum_ay / num_samples; + offset_az = ((float)sum_az / num_samples) - 16384.0f; +} + +void mpu_read_data(MPU_Data_t *data) { + uint8_t raw_accel_gyro[14]; + uint8_t raw_mag[7]; // 6 data bytes + ST2 status byte + + // Read Accel (0x3B-0x40) and Gyro (0x43-0x48) + mpu_read(MPU_ADDR, 0x3B, raw_accel_gyro, 14); + + // Process Accelerometer + data->ax = ((float)((int16_t)((raw_accel_gyro[0] << 8) | raw_accel_gyro[1])) - offset_ax) / 16384.0f; + data->ay = ((float)((int16_t)((raw_accel_gyro[2] << 8) | raw_accel_gyro[3])) - offset_ay) / 16384.0f; + data->az = ((float)((int16_t)((raw_accel_gyro[4] << 8) | raw_accel_gyro[5])) - offset_az) / 16384.0f; + + // Process Gyroscope (Scale 131.0 for +/- 250 deg/s) + data->gx = (float)((int16_t)((raw_accel_gyro[8] << 8) | raw_accel_gyro[9])) / 131.0f; + data->gy = (float)((int16_t)((raw_accel_gyro[10] << 8) | raw_accel_gyro[11])) / 131.0f; + data->gz = (float)((int16_t)((raw_accel_gyro[12] << 8) | raw_accel_gyro[13])) / 131.0f; + + // Read Magnetometer + mpu_read(MAG_ADDR, 0x03, raw_mag, 7); + // AK8963 is little-endian + data->mx = (float)((int16_t)((raw_mag[1] << 8) | raw_mag[0])); + data->my = (float)((int16_t)((raw_mag[3] << 8) | raw_mag[2])); + data->mz = (float)((int16_t)((raw_mag[5] << 8) | raw_mag[4])); +} \ No newline at end of file diff --git a/mpu_handler.h b/mpu_handler.h new file mode 100644 index 0000000..2755877 --- /dev/null +++ b/mpu_handler.h @@ -0,0 +1,25 @@ +#ifndef MPU_HANDLER_H +#define MPU_HANDLER_H + +#include "pico/stdlib.h" +#include "hardware/i2c.h" + +#define I2C_PORT i2c0 +#define SDA_PIN 4 +#define SCL_PIN 5 +#define MPU_ADDR 0x68 +#define MAG_ADDR 0x0C + +typedef struct { + float ax, ay, az; + float gx, gy, gz; + float mx, my, mz; +} MPU_Data_t; + +void mpu_init(void); + +void mpu_calibrate(void); + +void mpu_read_data(MPU_Data_t *data); + +#endif //MPU_HANDLER_H \ No newline at end of file diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake new file mode 100644 index 0000000..65f8a6f --- /dev/null +++ b/pico_sdk_import.cmake @@ -0,0 +1,73 @@ +# 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 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 () + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + GIT_SUBMODULES_RECURSE FALSE + ) + else () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + endif () + + 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/server.c b/server.c new file mode 100644 index 0000000..764277d --- /dev/null +++ b/server.c @@ -0,0 +1,52 @@ +#include +#include "pico/stdlib.h" +#include "ble_server.h" +#include "mpu_handler.h" +#include "gps_handler.h" + +#define GENERIC_MESSAGE_SEND_INTERVAL_MS 100 + +static repeating_timer_t timer; +static char ble_buffer[64]; + +static MPU_Data_t mpu_values; +static GPS_Data_t gps_values; + +bool periodic_message_timer_callback(repeating_timer_t *rt) { + (void)rt; + + // Data Format: S,AccX,AccY,AccZ,Lat,Lon,Fix + snprintf(ble_buffer, sizeof(ble_buffer), + "S,%.2f,%.2f,%.2f,%.4f,%.4f,%d", + mpu_values.ax, mpu_values.ay, mpu_values.az, + gps_values.latitude, gps_values.longitude, + gps_values.has_fix ? 1 : 0); + + send_message(ble_buffer); + + return true; +} + +int main() { + stdio_init_all(); + sleep_ms(1000); + + start_server(); + mpu_init(); + mpu_calibrate(); + gps_init(); + + if (!add_repeating_timer_ms(GENERIC_MESSAGE_SEND_INTERVAL_MS, periodic_message_timer_callback, NULL, &timer)) { + printf("Failed to start timer!\n"); + } + + while (true) { + mpu_read_data(&mpu_values); + gps_update(&gps_values); + sleep_ms(50); + } + + cancel_repeating_timer(&timer); + stop_server(); + return 0; +} \ No newline at end of file