Skip to content

Commit

Permalink
High sampling rate multicore data logger
Browse files Browse the repository at this point in the history
  • Loading branch information
oyama committed Jun 19, 2024
1 parent d4172d8 commit e3afff4
Show file tree
Hide file tree
Showing 12 changed files with 198 additions and 23 deletions.
5 changes: 3 additions & 2 deletions EXAMPLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
|-----------------|-------------------------------------------------------------------------|
| [hello](examples/hello) | Hello filesystem world. |
| [fs\_inits](examples/fs_inits) | Examples of file system layout combinations. |
| [usb\_msc\_logger](examples/usb_msc_logger) | Data logger that mounts littlefs and FAT on flash memory and shares it with a PC via USB mass storage class.|
| [multicore\_logger](examples/multicore_logger) | Store high sampling rate sensor data on SD cards. |
| [elastic\_mqtt\_client](examples/elastic_mqtt_client) |Implements an MQTT client with a local queue to handle network disconnections seamlessly. |
| [usb\_logger](examples/usb_logger) | Data logger that mounts littlefs and FAT on flash memory and shares it with a PC via USB mass storage class.|
| [benchmark](examples/benchmark)| Data transfer tests with different block devices and different file system combinations.|

## Building sample code
Expand All @@ -17,7 +18,7 @@ Firmware can be built from the _CMake_ build directory of _pico-vfs_.
```bash
mkdir build; cd build/
PICO_SDK_PATH=/path/to/pico-sdk cmake ..
make hello fs_init_example logger benchmark
make hello fs_init_example multicore_logger usb_logger benchmark
```

The `elastic_mqtt_client` uses Wi-Fi, so `PICO_BOARD=pico_w` needs to be specified. Furthermore, `WIFI_SSID`, `WIFI_PASSWORD`, `MQTT_USER` and `MQTT_PASSWORD` must be specified in the environment variables when running cmake. This example uses [Adafruit IO](https://io.adafruit.com/)'s MQTT server.
Expand Down
3 changes: 2 additions & 1 deletion examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
add_subdirectory(hello EXCLUDE_FROM_ALL)
add_subdirectory(fs_inits EXCLUDE_FROM_ALL)
add_subdirectory(usb_msc_logger EXCLUDE_FROM_ALL)
add_subdirectory(multicore_logger EXCLUDE_FROM_ALL)
add_subdirectory(usb_logger EXCLUDE_FROM_ALL)
add_subdirectory(benchmark EXCLUDE_FROM_ALL)

if (PICO_CYW43_SUPPORTED)
Expand Down
12 changes: 12 additions & 0 deletions examples/multicore_logger/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
add_executable(multicore_logger main.c fs_init.c)
target_link_libraries(multicore_logger PRIVATE
pico_stdlib
pico_util
pico_multicore
blockdevice_sd
filesystem_fat
filesystem_vfs
)
pico_enable_stdio_usb(multicore_logger 1)
pico_enable_filesystem(multicore_logger)
pico_add_extra_outputs(multicore_logger)
38 changes: 38 additions & 0 deletions examples/multicore_logger/fs_init.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2024, Hiroyuki OYAMA. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <string.h>
#include "blockdevice/flash.h"
#include "blockdevice/sd.h"
#include "filesystem/fat.h"
#include "filesystem/vfs.h"

bool fs_init(void) {
printf("fs_init FAT on SD card\n");
blockdevice_t *sd = blockdevice_sd_create(spi0,
PICO_DEFAULT_SPI_TX_PIN,
PICO_DEFAULT_SPI_RX_PIN,
PICO_DEFAULT_SPI_SCK_PIN,
PICO_DEFAULT_SPI_CSN_PIN,
10 * 1000 * 1000,
false);
filesystem_t *fat = filesystem_fat_create();
int err = fs_mount("/sd", fat, sd);
if (err == -1) {
printf("format /sd with FAT\n");
err = fs_format(fat, sd);
if (err == -1) {
printf("fs_format error: %s", strerror(errno));
return false;
}
err = fs_mount("/sd", fat, sd);
if (err == -1) {
printf("fs_mount error: %s", strerror(errno));
return false;
}
}
return true;
}
124 changes: 124 additions & 0 deletions examples/multicore_logger/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Multi-core, high sampling rate data logger example
*
* Copyright 2024, Hiroyuki OYAMA. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <math.h>
#include <pico/multicore.h>
#include <pico/stdlib.h>
#include <pico/time.h>
#include <pico/util/queue.h>
#include <stdio.h>
#include <string.h>
#include "filesystem/vfs.h"

#if !defined(SAMPLING_RATE_HZ)
#define SAMPLING_RATE_HZ 500
#endif

typedef struct {
float accel_x;
float accel_y;
float accel_z;
float gyro_x;
float gyro_y;
float gyro_z;
float mag_x;
float mag_y;
float mag_z;
uint64_t timestamp;
} sensor_data_t;

queue_t sensor_queue;

float normal_random(float mean, float stddev) {
static int hasSpare = 0;
static float spare;

if (hasSpare) {
hasSpare = 0;
return mean + stddev * spare;
}

hasSpare = 1;
float u, v, s;
do {
u = (float)rand() / RAND_MAX * 2.0f - 1.0f;
v = (float)rand() / RAND_MAX * 2.0f - 1.0f;
s = u * u + v * v;
} while (s >= 1.0f || s == 0.0f);

s = sqrtf(-2.0f * logf(s) / s);
spare = v * s;
return mean + stddev * u * s;
}

static void produce_sensor_data_task(void) {
sensor_data_t entry = {0};
while (1) {
entry.timestamp = (uint64_t)get_absolute_time();
entry.accel_x = normal_random(0, 0.01);
entry.accel_y = normal_random(0, 0.01);
entry.accel_z = normal_random(-1.0, 0.01);
entry.gyro_x = normal_random(0, 0.001);
entry.gyro_y = normal_random(0, 0.001);
entry.gyro_z = normal_random(0, 0.001);
entry.mag_x = normal_random(-0.25, 0.0001);
entry.mag_y = normal_random(0.1, 0.01);
entry.mag_z = normal_random(0.4, 0.01);

queue_add_blocking(&sensor_queue, &entry);

absolute_time_t before = entry.timestamp + (uint64_t)((1.0 / SAMPLING_RATE_HZ) * 1000000);
while (absolute_time_diff_us(before, get_absolute_time()) <= 0)
tight_loop_contents();
}
}

int main(void) {
stdio_init_all();
if (!fs_init()) {
printf("SD card device not found\n");
return -1;
}

queue_init(&sensor_queue, sizeof(sensor_data_t), 1024);
FILE *fp = fopen("/sd/sensor_data.csv", "w");
if (fp == NULL) {
printf("fopen failed: %s\n", strerror(errno));
}
fprintf(fp, "Time,AccelX,AccelY,AccelZ,GyroX,GyroY,GyroZ,MagX,MagY,Magz\n");

multicore_reset_core1();
sleep_ms(100);
multicore_launch_core1(produce_sensor_data_task);

sensor_data_t entry = {0};
absolute_time_t last_checkpoint = get_absolute_time();
uint32_t n = 0;
while (1) {
queue_remove_blocking(&sensor_queue, &entry);
fprintf(fp, "%.6f,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f,%.6f\n",
(double)entry.timestamp / 1000 / 1000,
entry.accel_x, entry.accel_y, entry.accel_z,
entry.gyro_x, entry.gyro_y, entry.gyro_x,
entry.mag_x, entry.mag_y, entry.mag_z);
n += 1;
absolute_time_t now = get_absolute_time();
int64_t duration = absolute_time_diff_us(last_checkpoint, now);
if (duration >= 1000000) {
printf("Store %lu samples/sec, Remaining queue %u\n",
n, queue_get_level(&sensor_queue));
n = 0;
last_checkpoint = now;
}
}

fclose(fp);
queue_free(&sensor_queue);

while (1)
tight_loop_contents();
}
19 changes: 19 additions & 0 deletions examples/usb_logger/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# USB MSC Logger demo
add_executable(usb_logger
main.c
usb_msc.c
usb_descriptors.c
)
target_include_directories(usb_logger PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(usb_logger PRIVATE
pico_stdlib
hardware_adc
blockdevice_flash
filesystem_fat
filesystem_littlefs
filesystem_vfs
tinyusb_board
tinyusb_device
)
pico_enable_stdio_usb(usb_logger 1)
pico_add_extra_outputs(usb_logger)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
20 changes: 0 additions & 20 deletions examples/usb_msc_logger/CMakeLists.txt

This file was deleted.

0 comments on commit e3afff4

Please sign in to comment.