or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

device-motion.mdenvironmental-sensors.mdindex.mdmagnetic-sensors.mdmotion-sensors.mdpedometer.md
tile.json

pedometer.mddocs/

Step Counting

Pedometer functionality for fitness and health applications with step counting and historical data access.

Capabilities

Step Counting Functions

Provides access to device step counting capabilities for fitness tracking and health applications.

/**
 * Subscribe to pedometer updates for real-time step counting
 * @param callback - Callback invoked when new step count data is available
 * @returns Subscription that enables calling remove() to unsubscribe
 */
function watchStepCount(callback: PedometerUpdateCallback): Subscription;

/**
 * Get the step count between two dates (iOS only)
 * @param start - Date indicating the start of the range over which to measure steps
 * @param end - Date indicating the end of the range over which to measure steps
 * @returns Promise that fulfills with PedometerResult
 * @platform ios
 */
function getStepCountAsync(start: Date, end: Date): Promise<PedometerResult>;

/**
 * Returns whether the pedometer is enabled on the device
 * @returns Promise that fulfills with boolean indicating pedometer availability
 */
function isAvailableAsync(): Promise<boolean>;

/**
 * Checks user's permissions for accessing pedometer
 * @returns Promise resolving to PermissionResponse
 */
function getPermissionsAsync(): Promise<PermissionResponse>;

/**
 * Asks the user to grant permissions for accessing pedometer
 * @returns Promise resolving to PermissionResponse
 */
function requestPermissionsAsync(): Promise<PermissionResponse>;

interface PedometerResult {
  /** Number of steps taken between the given dates */
  steps: number;
}

type PedometerUpdateCallback = (result: PedometerResult) => void;

Basic Usage Examples:

import { Pedometer } from "expo-sensors";

// Check if pedometer is available
const isAvailable = await Pedometer.isAvailableAsync();
if (!isAvailable) {
  console.log("Pedometer not available on this device");
  return;
}

// Request permissions (primarily for iOS)
const { status } = await Pedometer.requestPermissionsAsync();
if (status !== 'granted') {
  console.log("Pedometer permission not granted");
  return;
}

// Subscribe to real-time step updates
const subscription = Pedometer.watchStepCount(({ steps }) => {
  console.log(`Steps taken: ${steps}`);
});

// Clean up subscription when done
subscription.remove();

Historical Step Data (iOS only):

import { Pedometer } from "expo-sensors";

// Get steps for the last 24 hours
const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
const now = new Date();

try {
  const result = await Pedometer.getStepCountAsync(oneDayAgo, now);
  console.log(`Steps in last 24 hours: ${result.steps}`);
} catch (error) {
  console.log("Error fetching step count:", error);
}

// Get steps for specific time periods
async function getStepsForPeriod(days: number): Promise<number> {
  const startDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
  const endDate = new Date();
  
  try {
    const result = await Pedometer.getStepCountAsync(startDate, endDate);
    return result.steps;
  } catch (error) {
    console.log(`Error getting steps for ${days} days:`, error);
    return 0;
  }
}

// Usage examples
const todaySteps = await getStepsForPeriod(1);
const weekSteps = await getStepsForPeriod(7);
console.log(`Today: ${todaySteps} steps, This week: ${weekSteps} steps`);

Fitness Tracking Applications

Daily Step Goal Tracker:

import { Pedometer } from "expo-sensors";

interface StepGoal {
  daily: number;
  weekly: number;
  progress: number; // 0-1 (percentage)
}

class StepTracker {
  private dailyGoal = 10000;
  private weeklyGoal = 70000;
  private currentSteps = 0;
  
  async startTracking(onProgress: (goal: StepGoal) => void) {
    // Check availability and permissions
    const isAvailable = await Pedometer.isAvailableAsync();
    if (!isAvailable) {
      throw new Error("Pedometer not available");
    }
    
    const { status } = await Pedometer.requestPermissionsAsync();
    if (status !== 'granted') {
      throw new Error("Pedometer permission denied");
    }
    
    // Subscribe to real-time updates
    const subscription = Pedometer.watchStepCount(({ steps }) => {
      this.currentSteps = steps;
      const progress = Math.min(steps / this.dailyGoal, 1);
      
      onProgress({
        daily: this.dailyGoal,
        weekly: this.weeklyGoal,
        progress: progress
      });
      
      // Check if goal is reached
      if (steps >= this.dailyGoal && progress >= 1) {
        this.onGoalReached();
      }
    });
    
    return subscription;
  }
  
  setDailyGoal(steps: number) {
    this.dailyGoal = steps;
  }
  
  private onGoalReached() {
    console.log(`πŸŽ‰ Daily goal of ${this.dailyGoal} steps reached!`);
    // Trigger notification or celebration animation
  }
  
  async getWeeklyStats(): Promise<{ totalSteps: number; dailyAverage: number; goalProgress: number }> {
    try {
      const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
      const now = new Date();
      const result = await Pedometer.getStepCountAsync(weekAgo, now);
      
      const totalSteps = result.steps;
      const dailyAverage = Math.round(totalSteps / 7);
      const goalProgress = Math.min(totalSteps / this.weeklyGoal, 1);
      
      return { totalSteps, dailyAverage, goalProgress };
    } catch (error) {
      console.log("Error getting weekly stats:", error);
      return { totalSteps: 0, dailyAverage: 0, goalProgress: 0 };
    }
  }
}

// Usage
const tracker = new StepTracker();
tracker.setDailyGoal(12000);

const subscription = await tracker.startTracking(({ daily, progress }) => {
  const percentage = Math.round(progress * 100);
  console.log(`Progress: ${percentage}% of ${daily} daily step goal`);
});

Activity Level Monitor:

import { Pedometer } from "expo-sensors";

type ActivityLevel = 'sedentary' | 'lightly_active' | 'moderately_active' | 'very_active';

class ActivityMonitor {
  private stepHistory: Array<{ timestamp: number; steps: number }> = [];
  private lastStepCount = 0;
  
  startMonitoring(onActivityChange: (level: ActivityLevel, stepsPerMinute: number) => void) {
    const subscription = Pedometer.watchStepCount(({ steps }) => {
      const now = Date.now();
      const stepsSinceLastUpdate = steps - this.lastStepCount;
      
      // Record step data with timestamp
      this.stepHistory.push({ timestamp: now, steps: stepsSinceLastUpdate });
      this.lastStepCount = steps;
      
      // Keep only last 10 minutes of data
      const tenMinutesAgo = now - 10 * 60 * 1000;
      this.stepHistory = this.stepHistory.filter(entry => entry.timestamp > tenMinutesAgo);
      
      // Calculate activity level
      const activityLevel = this.calculateActivityLevel();
      const stepsPerMinute = this.calculateStepsPerMinute();
      
      onActivityChange(activityLevel, stepsPerMinute);
    });
    
    return subscription;
  }
  
  private calculateActivityLevel(): ActivityLevel {
    if (this.stepHistory.length < 2) return 'sedentary';
    
    const stepsPerMinute = this.calculateStepsPerMinute();
    
    if (stepsPerMinute < 1) return 'sedentary';
    if (stepsPerMinute < 30) return 'lightly_active';
    if (stepsPerMinute < 60) return 'moderately_active';
    return 'very_active';
  }
  
  private calculateStepsPerMinute(): number {
    if (this.stepHistory.length < 2) return 0;
    
    const totalSteps = this.stepHistory.reduce((sum, entry) => sum + entry.steps, 0);
    const timeSpan = this.stepHistory[this.stepHistory.length - 1].timestamp - this.stepHistory[0].timestamp;
    const minutes = timeSpan / (1000 * 60);
    
    return minutes > 0 ? totalSteps / minutes : 0;
  }
}

// Usage
const activityMonitor = new ActivityMonitor();
const subscription = activityMonitor.startMonitoring((level, stepsPerMinute) => {
  console.log(`Activity Level: ${level}, Steps/min: ${stepsPerMinute.toFixed(1)}`);
  
  // Provide feedback based on activity level
  switch (level) {
    case 'sedentary':
      console.log("πŸ’Ί Consider taking a short walk!");
      break;
    case 'lightly_active':
      console.log("🚢 Nice gentle movement!");
      break;
    case 'moderately_active':
      console.log("πŸƒ Great activity level!");
      break;
    case 'very_active':
      console.log("⚑ Excellent workout pace!");
      break;
  }
});

Health Integration Example:

import { Pedometer } from "expo-sensors";

interface HealthMetrics {
  steps: number;
  estimatedCalories: number;
  estimatedDistance: number; // in kilometers
  activeMinutes: number;
}

class HealthTracker {
  private userWeight = 70; // kg (default)
  private strideLength = 0.76; // meters (default)
  private startTime = Date.now();
  private totalSteps = 0;
  
  setUserProfile(weight: number, strideLength: number) {
    this.userWeight = weight;
    this.strideLength = strideLength;
  }
  
  startHealthTracking(onUpdate: (metrics: HealthMetrics) => void) {
    const subscription = Pedometer.watchStepCount(({ steps }) => {
      this.totalSteps = steps;
      const metrics = this.calculateHealthMetrics();
      onUpdate(metrics);
    });
    
    return subscription;
  }
  
  private calculateHealthMetrics(): HealthMetrics {
    const steps = this.totalSteps;
    
    // Estimate calories burned (rough calculation)
    // Formula: steps * weight(kg) * 0.0005
    const estimatedCalories = Math.round(steps * this.userWeight * 0.0005);
    
    // Estimate distance
    const estimatedDistance = (steps * this.strideLength) / 1000; // Convert to km
    
    // Calculate active minutes (assuming 100+ steps per minute indicates activity)
    const elapsedMinutes = (Date.now() - this.startTime) / (1000 * 60);
    const averageStepsPerMinute = steps / elapsedMinutes;
    const activeMinutes = averageStepsPerMinute > 100 ? Math.round(elapsedMinutes * 0.7) : 0;
    
    return {
      steps,
      estimatedCalories,
      estimatedDistance: Math.round(estimatedDistance * 100) / 100, // 2 decimal places
      activeMinutes
    };
  }
  
  async getDailyReport(): Promise<HealthMetrics> {
    try {
      const startOfDay = new Date();
      startOfDay.setHours(0, 0, 0, 0);
      const now = new Date();
      
      const result = await Pedometer.getStepCountAsync(startOfDay, now);
      
      const steps = result.steps;
      const estimatedCalories = Math.round(steps * this.userWeight * 0.0005);
      const estimatedDistance = Math.round((steps * this.strideLength / 1000) * 100) / 100;
      
      // Estimate active minutes for the day
      const hoursElapsed = (now.getTime() - startOfDay.getTime()) / (1000 * 60 * 60);
      const activeMinutes = Math.round(hoursElapsed * 0.1 * (steps / 1000)); // Rough estimate
      
      return { steps, estimatedCalories, estimatedDistance, activeMinutes };
    } catch (error) {
      console.log("Error getting daily report:", error);
      return { steps: 0, estimatedCalories: 0, estimatedDistance: 0, activeMinutes: 0 };
    }
  }
}

// Usage
const healthTracker = new HealthTracker();
healthTracker.setUserProfile(75, 0.78); // 75kg, 78cm stride

const subscription = healthTracker.startHealthTracking((metrics) => {
  console.log(`πŸ“Š Health Metrics:`);
  console.log(`   Steps: ${metrics.steps}`);
  console.log(`   Calories: ${metrics.estimatedCalories} kcal`);
  console.log(`   Distance: ${metrics.estimatedDistance} km`);
  console.log(`   Active: ${metrics.activeMinutes} min`);
});

Platform Considerations

Android

  • Pedometer functionality available on devices with step counter sensors
  • Real-time step updates work well
  • Historical data limited compared to iOS
  • Requires physical activity or motion for accurate counting

iOS

  • Comprehensive pedometer support through Core Motion framework
  • Historical data available for up to 7 days
  • High accuracy step counting
  • Background step counting supported
  • iOS Health app integration possible

Web

  • Pedometer functionality not available on web platforms
  • Consider using device motion sensors for basic step detection approximations

Important Notes

Background Updates:

  • On Android: Pedometer updates will not be delivered while the app is in the background
  • On iOS: Background step counting is supported, and historical data can be retrieved

Data Limitations:

  • iOS: Historical data is limited to the past 7 days
  • Android: Real-time updates only, limited historical data storage

Alternative Solutions:

  • Android: Consider using Health Connect API for more comprehensive health data
  • iOS: Integrate with HealthKit for full health ecosystem access