[go: up one dir, main page]

0% found this document useful (0 votes)
3 views9 pages

Setu Main CPP

The document outlines an Arduino-based firmware for a dual-core FreeRTOS system designed for real-time sensor data acquisition and processing. It includes configurations for various sensors, a state machine for system management, and tasks for sampling, processing, and communication. The system employs a hardware timer for precise sampling and utilizes double buffering to ensure continuous data flow without loss.

Uploaded by

jbontawar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views9 pages

Setu Main CPP

The document outlines an Arduino-based firmware for a dual-core FreeRTOS system designed for real-time sensor data acquisition and processing. It includes configurations for various sensors, a state machine for system management, and tasks for sampling, processing, and communication. The system employs a hardware timer for precise sampling and utilizes double buffering to ensure continuous data flow without loss.

Uploaded by

jbontawar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 9

#include <Arduino.

h>
#include <esp_task_wdt.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>

// --- System Configuration Constants ---


#define SAMPLING_FREQUENCY_HZ 200 // Target sampling rate
#define WDT_TIMEOUT_S 10 // Task Watchdog Timer timeout
#define BUFFER_SIZE 1024 // Number of samples per buffer
#define QUEUE_SIZE 2 // Double buffering queue size

// --- GPIO Pin Definitions (Hardware Contract) ---


// I2C Bus (MPU-6050)
#define MPU_SCL_PIN 39
#define MPU_SDA_PIN 40

// Digital I/O (HX711)


#define HX711_DT_PIN 5
#define HX711_SCK_PIN 4

// 1-Wire Protocol (DS18B20)


#define DS18B20_DQ_PIN 17

// SPI Bus (LoRa Ra-02)


#define LORA_NSS_PIN 10
#define LORA_MOSI_PIN 11
#define LORA_SCK_PIN 12
#define LORA_MISO_PIN 13

// --- System State Machine ---


enum SystemState {
INITIALIZING,
SAMPLING,
PROCESSING,
TRANSMITTING,
LOW_POWER_IDLE
};

volatile SystemState systemState = INITIALIZING;

// --- Sensor Data Buffer Structure ---


struct SensorDataBuffer {
uint32_t timestamp; // Buffer timestamp
uint16_t sampleCount; // Number of valid samples
struct {
float accel_x, accel_y, accel_z; // MPU-6050 accelerometer data
float gyro_x, gyro_y, gyro_z; // MPU-6050 gyroscope data
int32_t strain; // HX711 strain reading
float temperature; // DS18B20 temperature
} samples[BUFFER_SIZE];
};

// --- Global State and Synchronization ---


// Double Buffers for sensor data
SensorDataBuffer bufferA;
SensorDataBuffer bufferB;
SensorDataBuffer* activeBuffer = &bufferA; // Currently being filled
SensorDataBuffer* processingBuffer = nullptr; // Currently being processed
// FreeRTOS Handles
TaskHandle_t samplingTaskHandle = nullptr;
TaskHandle_t processingTaskHandle = nullptr;
TaskHandle_t loraTaskHandle = nullptr;
TaskHandle_t mainControlTaskHandle = nullptr;

// Inter-core communication queue - passes buffer pointers from Core 0 to Core 1


QueueHandle_t dataBufferQueue = nullptr;

// Hardware Timer for deterministic sampling


hw_timer_t *samplingTimer = nullptr;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

// Buffer management variables


volatile uint16_t currentSampleIndex = 0;
volatile bool bufferReady = false;

// --- Minimalist Interrupt Service Routine (ISR) ---


// This ISR is kept minimal: its only job is to notify the sampling_task
void IRAM_ATTR onTimer() {
portENTER_CRITICAL_ISR(&timerMux);

// Signal the sampling task to acquire a new data point


// This is the ONLY operation performed in the ISR to minimize interrupt
latency
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(samplingTaskHandle, &xHigherPriorityTaskWoken);

// Yield to higher priority task if necessary


if (xHigherPriorityTaskWoken) {
portYIELD_FROM_ISR();
}

portEXIT_CRITICAL_ISR(&timerMux);
}

// --- FreeRTOS Task Implementations ---

/**
* Core 0 (PRO_CPU) - Real-Time Sampling Task
* Highest priority task dedicated to deterministic data acquisition
* Pinned to Core 0 for hardware-enforced isolation from application logic
*/
void sampling_task(void *pvParameters) {
Serial.println("[Core 0] Sampling task started");

// Task main loop - runs on Core 0 (Real-Time Core)


for (;;) {
// Wait for notification from hardware timer ISR, blocking indefinitely
// This ensures the task only runs when triggered by the precise hardware
timer
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

// --- HIGH-FREQUENCY DATA ACQUISITION LOGIC ---


// Critical Section: Time-sensitive sensor reading operations

// 1. Read MPU-6050 (IMU) data


// TODO: Implement I2C transaction to read 3-axis accelerometer and
gyroscope
// activeBuffer->samples[currentSampleIndex].accel_x = read_mpu_accel_x();
// activeBuffer->samples[currentSampleIndex].accel_y = read_mpu_accel_y();
// activeBuffer->samples[currentSampleIndex].accel_z = read_mpu_accel_z();
// activeBuffer->samples[currentSampleIndex].gyro_x = read_mpu_gyro_x();
// activeBuffer->samples[currentSampleIndex].gyro_y = read_mpu_gyro_y();
// activeBuffer->samples[currentSampleIndex].gyro_z = read_mpu_gyro_z();

// 2. Read HX711 (Strain) data - non-blocking check first


// TODO: Implement non-blocking strain reading using is_ready() pattern
// if (hx711_is_ready()) {
// activeBuffer->samples[currentSampleIndex].strain =
read_hx711_strain();
// }

// 3. Read DS18B20 (Temperature) data - periodically (slower changing)


// TODO: Implement periodic temperature reading (e.g., once per second)
// static uint32_t temp_counter = 0;
// if (++temp_counter >= SAMPLING_FREQUENCY_HZ) {
// activeBuffer->samples[currentSampleIndex].temperature =
read_ds18b20_temp();
// temp_counter = 0;
// }

// 4. Update sample metadata


activeBuffer->samples[currentSampleIndex].temperature = 25.0; //
Placeholder
activeBuffer->timestamp = millis();

// 5. Advance sample index and check for buffer full condition


currentSampleIndex++;

// 6. Buffer Management - Check if current buffer is full


if (currentSampleIndex >= BUFFER_SIZE) {
// Buffer is full - prepare for double-buffer swap
activeBuffer->sampleCount = currentSampleIndex;

// Send pointer to completed buffer to Core 1 processing task


// This is a non-blocking operation that passes the buffer pointer
if (xQueueSend(dataBufferQueue, &activeBuffer, 0) == pdPASS) {
// Successfully queued buffer for processing

// Immediately switch to alternate buffer to continue sampling


// This ensures continuous data acquisition without missing samples
activeBuffer = (activeBuffer == &bufferA) ? &bufferB : &bufferA;
currentSampleIndex = 0;

// Reset the new active buffer


activeBuffer->sampleCount = 0;
activeBuffer->timestamp = 0;
} else {
// Queue is full - this indicates Core 1 processing is falling
behind
// For now, continue overwriting current buffer (data loss
scenario)
Serial.println("[WARN] Core 1 processing queue full - potential
data loss");
currentSampleIndex = 0;
}
}

// Task returns to blocked state, waiting for next timer interrupt


// The hardware timer ensures precise, jitter-free sampling intervals
}
}

/**
* Core 1 (APP_CPU) - Signal Processing Task
* Handles computationally intensive DSP operations and feature extraction
* Pinned to Core 1 to prevent interference with real-time sampling
*/
void processing_task(void *pvParameters) {
Serial.println("[Core 1] Processing task started");

// Subscribe this task to the Task Watchdog Timer


esp_task_wdt_add(nullptr);

SensorDataBuffer* filledBuffer = nullptr;

// Task main loop - runs on Core 1 (Application Core)


for (;;) {
// Feed the Task Watchdog Timer to indicate this task is healthy
esp_task_wdt_reset();

// Wait for a full buffer pointer from the sampling_task on Core 0


// This blocks until Core 0 signals that a complete buffer is ready
if (xQueueReceive(dataBufferQueue, &filledBuffer, portMAX_DELAY) == pdPASS)
{
Serial.printf("[Core 1] Processing buffer with %d samples\n",
filledBuffer->sampleCount);

// --- COMPUTATIONALLY INTENSIVE DSP LOGIC ---


// The buffer is now safely accessible - Core 0 is writing to the other
buffer

// 1. PRIORITY: Vibration Analysis (FFT) - Safety Critical


// Process accelerometer data for structural resonance detection
// TODO: Apply Hann windowing function to reduce spectral leakage
// TODO: Perform FFT on windowed accelerometer data
// TODO: Extract dominant frequencies and amplitudes
// TODO: Check for dangerous resonance conditions (immediate alert)

// 2. Strain Analysis Pipeline


// TODO: Apply thermal compensation to all strain readings
// for (int i = 0; i < filledBuffer->sampleCount; i++) {
// float corrected_strain = apply_thermal_compensation(
// filledBuffer->samples[i].strain,
// filledBuffer->samples[i].temperature
// );
// // Store corrected value back or in separate array
// }

// TODO: Perform Rainflow counting on thermally compensated strain data


// TODO: Update cumulative fatigue cycle histograms in RTC memory

// 3. Feature Extraction and Data Packetization


// TODO: Package extracted features into LoRa transmission payload
// TODO: Signal lora_task that data packet is ready for transmission
// Placeholder processing delay to simulate computational load
vTaskDelay(pdMS_TO_TICKS(10));

Serial.printf("[Core 1] Buffer processing complete\n");


}

// Continue monitoring for new buffers from Core 0


// The double-buffering mechanism ensures continuous data flow
}
}

/**
* Core 1 - LoRa Communication Task (Placeholder)
* Manages LoRa radio operations and data transmission
*/
void lora_task(void *pvParameters) {
Serial.println("[Core 1] LoRa task started");

for (;;) {
// TODO: Wait for signal from processing_task that data packet is ready
// TODO: Configure LoRa radio parameters (frequency, spreading factor,
etc.)
// TODO: Transmit data packet
// TODO: Wait for TxDone confirmation
// TODO: Put radio back into low-power mode
// TODO: Signal main_control_task that transmission cycle is complete

vTaskDelay(pdMS_TO_TICKS(1000)); // Placeholder delay


}
}

/**
* Main Control Task - System State Machine Management
* Handles overall system coordination and power management
*/
void main_control_task(void *pvParameters) {
Serial.println("[Core 1] Main control task started");

// Subscribe to watchdog timer


esp_task_wdt_add(nullptr);

for (;;) {
esp_task_wdt_reset(); // Feed watchdog

// TODO: Implement main system state machine


switch (systemState) {
case INITIALIZING:
// Handled in setup()
systemState = SAMPLING;
break;

case SAMPLING:
// Monitor sampling progress, manage timeouts
// Transition to PROCESSING when buffer thresholds are met
break;

case PROCESSING:
// Monitor processing task progress
// Transition to TRANSMITTING when features are extracted
break;

case TRANSMITTING:
// Monitor LoRa transmission progress
// Transition to LOW_POWER_IDLE when transmission complete
break;

case LOW_POWER_IDLE:
// Prepare for and enter deep sleep mode
// TODO: Configure wake-up timer
// TODO: Store critical state in RTC memory
// esp_sleep_enable_timer_wakeup(SLEEP_DURATION_S * 1000000ULL);
// esp_deep_sleep_start();
Serial.println("Would enter deep sleep here...");
vTaskDelay(pdMS_TO_TICKS(5000)); // Placeholder
systemState = SAMPLING; // Resume cycle
break;
}

vTaskDelay(pdMS_TO_TICKS(100)); // Yield to other tasks


}
}

// --- Main Setup and Loop ---

void setup() {
Serial.begin(115200);
delay(2000); // Allow serial monitor to connect

Serial.println("=== SETU Super-Node Firmware Starting ===");


Serial.println("Dual-Core FreeRTOS Architecture with Hardware Timer Sampling");

// --- Initialize Task Watchdog Timer ---


Serial.println("Initializing Task Watchdog Timer...");
esp_task_wdt_init(WDT_TIMEOUT_S, true); // Enable panic on timeout
esp_task_wdt_add(nullptr); // Subscribe setup/loop task

// --- Initialize Inter-Core Communication ---


Serial.println("Creating FreeRTOS synchronization primitives...");

// Create queue for passing buffer pointers from Core 0 to Core 1


dataBufferQueue = xQueueCreate(QUEUE_SIZE, sizeof(SensorDataBuffer*));
if (dataBufferQueue == nullptr) {
Serial.println("ERROR: Failed to create data buffer queue");
while(1); // Halt on critical failure
}

// --- Initialize Peripheral Hardware ---


Serial.println("Initializing peripheral hardware...");
// TODO: Initialize I2C bus for MPU-6050
// TODO: Initialize SPI bus for LoRa module
// TODO: Initialize digital I/O for HX711
// TODO: Initialize 1-Wire bus for DS18B20

Serial.println("Hardware initialization complete (placeholder)");

// --- Configure Hardware Timer for Deterministic Sampling ---


Serial.println("Configuring hardware timer for deterministic sampling...");
// Use timer 0 with 80 prescaler (80MHz APB clock / 80 = 1MHz timer clock)
// This gives us 1μs resolution, suitable for precise timing control
samplingTimer = timerBegin(0, 80, true);

if (samplingTimer == nullptr) {
Serial.println("ERROR: Failed to initialize hardware timer");
while(1); // Halt on critical failure
}

// Attach our minimalist ISR to the timer


timerAttachInterrupt(samplingTimer, &onTimer, true);

// Calculate alarm value for desired sampling frequency


// For 200 Hz: 1,000,000 μs / 200 Hz = 5,000 μs per sample
uint32_t timerAlarmValue = 1000000 / SAMPLING_FREQUENCY_HZ;
Serial.printf("Setting timer alarm for %d Hz sampling (alarm value: %d μs)\n",
SAMPLING_FREQUENCY_HZ, timerAlarmValue);

timerAlarmWrite(samplingTimer, timerAlarmValue, true); // Auto-reload enabled

// --- Create FreeRTOS Tasks with Core Affinity ---


Serial.println("Creating FreeRTOS tasks with core affinity...");

// Create Core 0 (Real-Time) sampling task - HIGHEST PRIORITY


BaseType_t result = xTaskCreatePinnedToCore(
sampling_task, // Task function
"Sampling Task", // Task name (for debugging)
8192, // Stack size (bytes)
nullptr, // Task parameters
configMAX_PRIORITIES - 1, // Priority (highest possible)
&samplingTaskHandle, // Task handle
0 // Core affinity: Core 0 (PRO_CPU)
);

if (result != pdPASS) {
Serial.println("ERROR: Failed to create sampling task");
while(1);
}

// Create Core 1 (Application) processing task - HIGH PRIORITY


result = xTaskCreatePinnedToCore(
processing_task, // Task function
"Processing Task", // Task name
16384, // Stack size (larger for DSP operations)
nullptr, // Task parameters
5, // Priority (high, but lower than sampling)
&processingTaskHandle, // Task handle
1 // Core affinity: Core 1 (APP_CPU)
);

if (result != pdPASS) {
Serial.println("ERROR: Failed to create processing task");
while(1);
}

// Create Core 1 LoRa communication task - MEDIUM PRIORITY


result = xTaskCreatePinnedToCore(
lora_task, // Task function
"LoRa Task", // Task name
8192, // Stack size
nullptr, // Task parameters
4, // Priority (medium)
&loraTaskHandle, // Task handle
1 // Core affinity: Core 1 (APP_CPU)
);

if (result != pdPASS) {
Serial.println("ERROR: Failed to create LoRa task");
while(1);
}

// Create Core 1 main control task - LOWER PRIORITY


result = xTaskCreatePinnedToCore(
main_control_task, // Task function
"Main Control Task", // Task name
8192, // Stack size
nullptr, // Task parameters
3, // Priority (lower than processing/LoRa)
&mainControlTaskHandle, // Task handle
1 // Core affinity: Core 1 (APP_CPU)
);

if (result != pdPASS) {
Serial.println("ERROR: Failed to create main control task");
while(1);
}

// --- Start the Hardware Timer ---


Serial.println("Starting deterministic sampling timer...");
timerAlarmEnable(samplingTimer);

// --- System Ready ---


systemState = SAMPLING;
Serial.println("=== SETU Super-Node Firmware Ready ===");
Serial.printf("Core 0: Real-time sampling at %d Hz\n", SAMPLING_FREQUENCY_HZ);
Serial.println("Core 1: Signal processing, LoRa communication, and system
control");
Serial.println("Hardware timer ISR configured for deterministic data
acquisition");
Serial.println("Double-buffering mechanism active for zero-copy inter-core
communication");
}

void loop() {
// The main Arduino loop() represents the main_control_task functionality
// In this architecture, most work is done by dedicated FreeRTOS tasks
// The loop() is kept minimal and primarily feeds the watchdog

esp_task_wdt_reset(); // Feed watchdog from main loop

// Optional: Print system status periodically


static uint32_t lastStatusPrint = 0;
if (millis() - lastStatusPrint > 10000) { // Every 10 seconds
Serial.printf("[STATUS] System State: %d, Active Buffer: %s, Sample Index:
%d\n",
systemState,
(activeBuffer == &bufferA) ? "A" : "B",
currentSampleIndex);

// Print memory usage


Serial.printf("[MEMORY] Free heap: %d bytes, Min free heap: %d bytes\n",
ESP.getFreeHeap(), ESP.getMinFreeHeap());

lastStatusPrint = millis();
}

// Yield to other tasks - this prevents the Arduino loop from monopolizing CPU
vTaskDelay(pdMS_TO_TICKS(1000));
}

You might also like