diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..24f136b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*/*.wasm +*/wokwi-api.h diff --git a/heart_rate/Makefile b/heart_rate/Makefile new file mode 100644 index 0000000..cbb3f80 --- /dev/null +++ b/heart_rate/Makefile @@ -0,0 +1,75 @@ +# Wokwi Custom Chip Makefile +# Generated by wokwi-cli +# +# Usage: +# make - Build the chip +# make clean - Remove build artifacts +# make wokwi-api.h - Download the Wokwi API header +# +# Requirements: +# - WASI-SDK (https://github.com/WebAssembly/wasi-sdk) +# - Set WASI_SDK_PATH environment variable or install to ~/.wokwi/wasi-sdk +# + +# Configuration +CHIP_NAME ?= heartrate.chip +SOURCES ?= main.c +OUTPUT ?= $(CHIP_NAME).wasm + +# WASI-SDK paths +WASI_SDK_PATH ?= /l/disk0/alopes/.wokwi/wasi-sdk +WASI_SDK_VERSION ?= 25 + +# Compiler settings +CC = $(WASI_SDK_PATH)/bin/clang +SYSROOT = $(WASI_SDK_PATH)/share/wasi-sysroot + +# Compilation flags +CFLAGS = --target=wasm32-wasi \ + --sysroot=$(SYSROOT) \ + -nostartfiles \ + -I. \ + -O2 + +LDFLAGS = -Wl,--no-entry \ + -Wl,--import-memory \ + -Wl,--export-table + +# Default target +all: check-sdk wokwi-api.h $(OUTPUT) + +# Build the WASM file +$(OUTPUT): $(SOURCES) wokwi-api.h + @echo "Compiling $(CHIP_NAME)..." + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(SOURCES) + @echo "Success! Output: $@ ($$(du -h $@ | cut -f1))" + +# Check WASI-SDK is available +check-sdk: + @if [ ! -d "$(WASI_SDK_PATH)" ]; then \ + echo "Error: WASI-SDK not found at $(WASI_SDK_PATH)"; \ + echo ""; \ + echo "Install WASI-SDK:"; \ + echo " Option 1: Run 'wokwi-cli chip compile' once to auto-install"; \ + echo " Option 2: Download from https://github.com/WebAssembly/wasi-sdk/releases"; \ + echo " Option 3: Set WASI_SDK_PATH environment variable"; \ + exit 1; \ + fi + +# Download wokwi-api.h if not present +wokwi-api.h: + @if [ ! -f wokwi-api.h ]; then \ + echo "Downloading wokwi-api.h..."; \ + curl -sL "https://wokwi.com/api/chips/wokwi-api.h" -o wokwi-api.h; \ + echo "Downloaded wokwi-api.h"; \ + fi + +# Clean build artifacts +clean: + rm -f $(OUTPUT) + +# Clean everything including downloaded files +distclean: clean + rm -f wokwi-api.h + +.PHONY: all check-sdk clean distclean diff --git a/heart_rate/heartrate.chip.json b/heart_rate/heartrate.chip.json new file mode 100644 index 0000000..840a00c --- /dev/null +++ b/heart_rate/heartrate.chip.json @@ -0,0 +1,11 @@ +{ + "name": "heartrate", + "author": "Amaro Lopes", + "pins": [ + "VCC", + "GND", + "BTN", + "OUT" + ], + "controls": [] +} diff --git a/heart_rate/main.c b/heart_rate/main.c new file mode 100644 index 0000000..7296274 --- /dev/null +++ b/heart_rate/main.c @@ -0,0 +1,119 @@ + +#include "wokwi-api.h" +#include +#include + +/* -------------------------------------------------- */ +/* States */ + +typedef enum { STATE_NORMAL, STATE_RISING, STATE_FALLING } hr_state_t; + +typedef enum { STATE_SLOW = 0, STATE_FAST = 1 } hr_speed_t; + +/* -------------------------------------------------- */ +/* Chip state */ + +typedef struct { + pin_t out_pin; + pin_t btn_pin; + + float bpm; + int last_target; + + hr_state_t state; + hr_speed_t speed; + + int pulseValue; + + timer_t update_timer; +} chip_state_t; + +/* -------------------------------------------------- */ +/* Output mapping */ + +static void update_output_voltage(chip_state_t *chip) { + float volts = (chip->bpm * 3.3f) / 675.0f; + + if (volts < 0.0f) + volts = 0.0f; + if (volts > 3.3f) + volts = 3.3f; + + pin_dac_write(chip->out_pin, volts); + + printf("BPM: %.1f | Voltage: %.3fV\n", chip->bpm, volts); +} + +/* -------------------------------------------------- */ +/* Main update loop */ + +static void update_cb(void *user_data) { + chip_state_t *chip = user_data; + + int target = attr_read(chip->pulseValue); + + /* Read pull-down switch: LOW=SLOW, HIGH=FAST */ + chip->speed = pin_read(chip->btn_pin) ? STATE_FAST : STATE_SLOW; + + /* Detect target change and re-evaluate direction */ + if (target != chip->last_target) { + if (chip->bpm < target) { + chip->state = STATE_RISING; + } else if (chip->bpm > target) { + chip->state = STATE_FALLING; + } else { + chip->state = STATE_NORMAL; + } + chip->last_target = target; + } + + /* Step size */ + float step = (chip->speed == STATE_FAST) ? 2.0f : 0.5f; + + /* Rising */ + if (chip->state == STATE_RISING) { + chip->bpm += step; + if (chip->bpm >= target) { + chip->bpm = target; + chip->state = STATE_NORMAL; + } + } + + /* Falling */ + if (chip->state == STATE_FALLING) { + chip->bpm -= step; + if (chip->bpm <= target) { + chip->bpm = target; + chip->state = STATE_NORMAL; + } + } + + update_output_voltage(chip); +} + +/* -------------------------------------------------- */ +/* Init */ + +void chip_init(void) { + printf("Heart Rate chip init\n"); + + chip_state_t *chip = calloc(1, sizeof(chip_state_t)); + + /* Attribute: target BPM */ + chip->pulseValue = attr_init("pulseValue", 100); + + chip->bpm = 100.0f; + chip->last_target = 100; + chip->state = STATE_NORMAL; + chip->speed = STATE_SLOW; + + chip->out_pin = pin_init("OUT", ANALOG); + chip->btn_pin = pin_init("BTN", INPUT); // pull-down switch + + timer_config_t update_cfg = {.callback = update_cb, .user_data = chip}; + + chip->update_timer = timer_init(&update_cfg); + + /* 50 ms update rate */ + timer_start(chip->update_timer, 50000, true); +}