581 Mobile Phone Sensors: Web and Native APIs
581.1 Web-Based Mobile Sensing
581.1.1 Generic Sensor API (Web Standards)
Modern web browsers provide standardized APIs for accessing smartphone sensors without requiring native apps.
581.1.1.1 Accelerometer (Web API)
<!DOCTYPE html>
<html>
<head>
<title>Accelerometer Demo</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
}
.sensor-data {
font-size: 24px;
margin: 20px 0;
}
.axis {
padding: 10px;
margin: 5px 0;
background: #f0f0f0;
border-radius: 5px;
}
</style>
</head>
<body>
<h1>Accelerometer Sensor</h1>
<div id="status">Checking sensor support...</div>
<div class="sensor-data">
<div class="axis" id="x-axis">X: 0 m/s²</div>
<div class="axis" id="y-axis">Y: 0 m/s²</div>
<div class="axis" id="z-axis">Z: 0 m/s²</div>
</div>
<div id="activity">Activity: Unknown</div>
<script>
// Check if Generic Sensor API is supported
if ('Accelerometer' in window) {
document.getElementById('status').textContent = 'Accelerometer supported!';
try {
const accelerometer = new Accelerometer({ frequency: 60 });
accelerometer.addEventListener('reading', () => {
document.getElementById('x-axis').textContent =
`X: ${accelerometer.x.toFixed(2)} m/s²`;
document.getElementById('y-axis').textContent =
`Y: ${accelerometer.y.toFixed(2)} m/s²`;
document.getElementById('z-axis').textContent =
`Z: ${accelerometer.z.toFixed(2)} m/s²`;
// Detect activity
const magnitude = Math.sqrt(
accelerometer.x ** 2 +
accelerometer.y ** 2 +
accelerometer.z ** 2
);
let activity = 'Stationary';
if (magnitude > 12) {
activity = 'Shaking';
} else if (magnitude > 10) {
activity = 'Walking';
}
document.getElementById('activity').textContent =
`Activity: ${activity}`;
});
accelerometer.addEventListener('error', event => {
console.error('Accelerometer error:', event.error.name);
document.getElementById('status').textContent =
`Error: ${event.error.message}`;
});
accelerometer.start();
} catch (error) {
console.error('Failed to start accelerometer:', error);
document.getElementById('status').textContent =
`Error: ${error.message}`;
}
} else {
document.getElementById('status').textContent =
'Accelerometer not supported by your browser';
}
</script>
</body>
</html>581.1.1.2 Geolocation API
<!DOCTYPE html>
<html>
<head>
<title>GPS Location Tracker</title>
</head>
<body>
<h1>GPS Location Tracker</h1>
<button onclick="getCurrentLocation()">Get Current Location</button>
<button onclick="startTracking()">Start Tracking</button>
<button onclick="stopTracking()">Stop Tracking</button>
<div id="location-info"></div>
<script>
let watchId = null;
function getCurrentLocation() {
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition(
displayPosition,
handleError,
{
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
}
);
} else {
alert('Geolocation not supported');
}
}
function startTracking() {
if ('geolocation' in navigator) {
watchId = navigator.geolocation.watchPosition(
displayPosition,
handleError,
{
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
}
);
console.log('Started tracking with watchId:', watchId);
}
}
function stopTracking() {
if (watchId !== null) {
navigator.geolocation.clearWatch(watchId);
console.log('Stopped tracking');
watchId = null;
}
}
function displayPosition(position) {
const lat = position.coords.latitude;
const lon = position.coords.longitude;
const accuracy = position.coords.accuracy;
const altitude = position.coords.altitude;
const speed = position.coords.speed;
const heading = position.coords.heading;
const timestamp = new Date(position.timestamp);
const info = `
<h2>Location Data</h2>
<p><strong>Latitude:</strong> ${lat.toFixed(6)}</p>
<p><strong>Longitude:</strong> ${lon.toFixed(6)}</p>
<p><strong>Accuracy:</strong> ±${accuracy.toFixed(1)} meters</p>
<p><strong>Altitude:</strong> ${altitude ? altitude.toFixed(1) + ' m' : 'N/A'}</p>
<p><strong>Speed:</strong> ${speed ? speed.toFixed(1) + ' m/s' : 'N/A'}</p>
<p><strong>Heading:</strong> ${heading ? heading.toFixed(1) + '°' : 'N/A'}</p>
<p><strong>Timestamp:</strong> ${timestamp.toLocaleString()}</p>
<p><a href="https://www.google.com/maps?q=${lat},${lon}" target="_blank">View on Google Maps</a></p>
`;
document.getElementById('location-info').innerHTML = info;
// Send to IoT backend
sendLocationToBackend(lat, lon, accuracy, timestamp);
}
function handleError(error) {
let message = '';
switch(error.code) {
case error.PERMISSION_DENIED:
message = 'User denied geolocation permission';
break;
case error.POSITION_UNAVAILABLE:
message = 'Location information unavailable';
break;
case error.TIMEOUT:
message = 'Location request timeout';
break;
default:
message = 'Unknown error occurred';
}
document.getElementById('location-info').innerHTML =
`<p style="color: red;">Error: ${message}</p>`;
}
async function sendLocationToBackend(lat, lon, accuracy, timestamp) {
try {
const response = await fetch('https://iot-backend.example.com/api/location', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
latitude: lat,
longitude: lon,
accuracy: accuracy,
timestamp: timestamp.toISOString(),
deviceId: 'web-client-123'
})
});
if (response.ok) {
console.log('Location sent to backend successfully');
} else {
console.error('Failed to send location:', response.statusText);
}
} catch (error) {
console.error('Error sending location:', error);
}
}
</script>
</body>
</html>581.1.1.3 Device Orientation (Gyroscope/Magnetometer)
// DeviceOrientation API - combining accelerometer, gyroscope, magnetometer
window.addEventListener('deviceorientation', (event) => {
const alpha = event.alpha; // Z-axis rotation (0-360°) - compass
const beta = event.beta; // X-axis rotation (-180 to 180°) - front-to-back tilt
const gamma = event.gamma; // Y-axis rotation (-90 to 90°) - left-to-right tilt
console.log(`Alpha: ${alpha}°, Beta: ${beta}°, Gamma: ${gamma}°`);
// Compass heading (North = 0°)
const heading = 360 - alpha;
document.getElementById('compass').textContent = `Heading: ${heading.toFixed(0)}°`;
// Tilt detection
if (Math.abs(beta) > 45 || Math.abs(gamma) > 45) {
console.log('Device tilted significantly');
}
});
// Request permission on iOS 13+
if (typeof DeviceOrientationEvent.requestPermission === 'function') {
DeviceOrientationEvent.requestPermission()
.then(permissionState => {
if (permissionState === 'granted') {
window.addEventListener('deviceorientation', handleOrientation);
}
})
.catch(console.error);
}581.1.1.4 Progressive Web App (PWA) for IoT Sensing
<!DOCTYPE html>
<html>
<head>
<title>IoT Sensor PWA</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="manifest" href="manifest.json">
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.sensor-card {
background: #fff;
border: 1px solid #ddd;
border-radius: 8px;
padding: 15px;
margin: 10px 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.value {
font-size: 32px;
font-weight: bold;
color: #007bff;
}
button {
background: #007bff;
color: white;
border: none;
padding: 12px 24px;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
margin: 5px;
}
button:hover {
background: #0056b3;
}
#status {
padding: 10px;
margin: 10px 0;
border-radius: 5px;
background: #d4edda;
color: #155724;
}
</style>
</head>
<body>
<h1>IoT Multi-Sensor App</h1>
<div id="status">Offline</div>
<button onclick="startSensing()">Start Sensing</button>
<button onclick="stopSensing()">Stop Sensing</button>
<button onclick="sendData()">Send to Cloud</button>
<div class="sensor-card">
<h3>Accelerometer</h3>
<div class="value" id="accel">0.00 m/s²</div>
</div>
<div class="sensor-card">
<h3>Location</h3>
<div class="value" id="location">Unknown</div>
</div>
<div class="sensor-card">
<h3>Light Level</h3>
<div class="value" id="light">Unknown</div>
</div>
<div class="sensor-card">
<h3>Battery Level</h3>
<div class="value" id="battery">Unknown</div>
</div>
<script>
let sensorData = {
accelerometer: null,
location: null,
light: null,
battery: null,
timestamp: null
};
let accelerometer = null;
let lightSensor = null;
let geoWatchId = null;
// Service Worker for offline support
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('Service Worker registered'))
.catch(err => console.error('Service Worker registration failed:', err));
}
// Check online status
window.addEventListener('online', () => {
document.getElementById('status').textContent = 'Online';
document.getElementById('status').style.background = '#d4edda';
});
window.addEventListener('offline', () => {
document.getElementById('status').textContent = 'Offline - Data cached locally';
document.getElementById('status').style.background = '#f8d7da';
});
function startSensing() {
// Start accelerometer
if ('Accelerometer' in window) {
accelerometer = new Accelerometer({ frequency: 10 });
accelerometer.addEventListener('reading', () => {
const magnitude = Math.sqrt(
accelerometer.x ** 2 +
accelerometer.y ** 2 +
accelerometer.z ** 2
);
document.getElementById('accel').textContent =
`${magnitude.toFixed(2)} m/s²`;
sensorData.accelerometer = {
x: accelerometer.x,
y: accelerometer.y,
z: accelerometer.z,
magnitude: magnitude
};
});
accelerometer.start();
}
// Start GPS
geoWatchId = navigator.geolocation.watchPosition(
position => {
const lat = position.coords.latitude.toFixed(6);
const lon = position.coords.longitude.toFixed(6);
document.getElementById('location').textContent =
`${lat}, ${lon}`;
sensorData.location = {
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy
};
},
error => console.error('GPS error:', error),
{ enableHighAccuracy: true }
);
// Start ambient light sensor
if ('AmbientLightSensor' in window) {
lightSensor = new AmbientLightSensor();
lightSensor.addEventListener('reading', () => {
document.getElementById('light').textContent =
`${lightSensor.illuminance.toFixed(0)} lux`;
sensorData.light = lightSensor.illuminance;
});
lightSensor.start();
}
// Battery status
navigator.getBattery().then(battery => {
function updateBattery() {
const level = (battery.level * 100).toFixed(0);
const charging = battery.charging ? ' (Charging)' : '';
document.getElementById('battery').textContent =
`${level}%${charging}`;
sensorData.battery = {
level: battery.level,
charging: battery.charging
};
}
battery.addEventListener('levelchange', updateBattery);
battery.addEventListener('chargingchange', updateBattery);
updateBattery();
});
console.log('Sensors started');
}
function stopSensing() {
if (accelerometer) accelerometer.stop();
if (lightSensor) lightSensor.stop();
if (geoWatchId) navigator.geolocation.clearWatch(geoWatchId);
console.log('Sensors stopped');
}
async function sendData() {
sensorData.timestamp = new Date().toISOString();
try {
const response = await fetch('https://iot-backend.example.com/api/sensor-data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(sensorData)
});
if (response.ok) {
console.log('Data sent successfully');
alert('Data sent to cloud!');
} else {
// Cache data if offline
cacheData(sensorData);
alert('Data cached - will sync when online');
}
} catch (error) {
console.error('Error sending data:', error);
cacheData(sensorData);
alert('Data cached locally');
}
}
function cacheData(data) {
// Store in IndexedDB or localStorage
const cachedData = JSON.parse(localStorage.getItem('cachedSensorData') || '[]');
cachedData.push(data);
localStorage.setItem('cachedSensorData', JSON.stringify(cachedData));
}
</script>
</body>
</html>581.2 Native Mobile Applications
581.2.1 React Native (Cross-Platform)
// React Native IoT Sensor App
import React, { useState, useEffect } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { Accelerometer, Gyroscope, Magnetometer } from 'expo-sensors';
import * as Location from 'expo-location';
import { Audio } from 'expo-av';
export default function IoTSensorApp() {
const [accelData, setAccelData] = useState({ x: 0, y: 0, z: 0 });
const [gyroData, setGyroData] = useState({ x: 0, y: 0, z: 0 });
const [location, setLocation] = useState(null);
const [recording, setRecording] = useState(null);
const [isSensing, setIsSensing] = useState(false);
useEffect(() => {
if (isSensing) {
// Start accelerometer
const accelSubscription = Accelerometer.addListener(data => {
setAccelData(data);
});
Accelerometer.setUpdateInterval(100); // 100ms
// Start gyroscope
const gyroSubscription = Gyroscope.addListener(data => {
setGyroData(data);
});
Gyroscope.setUpdateInterval(100);
// Start location tracking
startLocationTracking();
return () => {
accelSubscription.remove();
gyroSubscription.remove();
};
}
}, [isSensing]);
const startLocationTracking = async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
console.log('Location permission denied');
return;
}
Location.watchPositionAsync(
{
accuracy: Location.Accuracy.High,
timeInterval: 5000,
distanceInterval: 10
},
(loc) => {
setLocation(loc.coords);
}
);
};
const startAudioRecording = async () => {
try {
const { status } = await Audio.requestPermissionsAsync();
if (status !== 'granted') {
console.log('Audio permission denied');
return;
}
await Audio.setAudioModeAsync({
allowsRecordingIOS: true,
playsInSilentModeIOS: true,
});
const { recording } = await Audio.Recording.createAsync(
Audio.RECORDING_OPTIONS_PRESET_HIGH_QUALITY
);
setRecording(recording);
console.log('Recording started');
} catch (err) {
console.error('Failed to start recording', err);
}
};
const stopAudioRecording = async () => {
await recording.stopAndUnloadAsync();
const uri = recording.getURI();
console.log('Recording saved to:', uri);
setRecording(null);
// Upload to server
uploadAudioFile(uri);
};
const uploadAudioFile = async (uri) => {
const formData = new FormData();
formData.append('audio', {
uri: uri,
type: 'audio/m4a',
name: 'recording.m4a',
});
try {
const response = await fetch('https://iot-backend.example.com/api/audio', {
method: 'POST',
body: formData,
});
console.log('Audio uploaded:', response.status);
} catch (error) {
console.error('Upload failed:', error);
}
};
const sendSensorData = async () => {
const data = {
accelerometer: accelData,
gyroscope: gyroData,
location: location,
timestamp: new Date().toISOString()
};
try {
const response = await fetch('https://iot-backend.example.com/api/sensor-data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
console.log('Data sent:', response.status);
} catch (error) {
console.error('Error sending data:', error);
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>IoT Sensor App</Text>
<View style={styles.sensorCard}>
<Text style={styles.sensorTitle}>Accelerometer</Text>
<Text>X: {accelData.x.toFixed(2)} m/s²</Text>
<Text>Y: {accelData.y.toFixed(2)} m/s²</Text>
<Text>Z: {accelData.z.toFixed(2)} m/s²</Text>
</View>
<View style={styles.sensorCard}>
<Text style={styles.sensorTitle}>Gyroscope</Text>
<Text>X: {gyroData.x.toFixed(2)} rad/s</Text>
<Text>Y: {gyroData.y.toFixed(2)} rad/s</Text>
<Text>Z: {gyroData.z.toFixed(2)} rad/s</Text>
</View>
<View style={styles.sensorCard}>
<Text style={styles.sensorTitle}>Location</Text>
{location && (
<>
<Text>Lat: {location.latitude.toFixed(6)}</Text>
<Text>Lon: {location.longitude.toFixed(6)}</Text>
<Text>Accuracy: ±{location.accuracy.toFixed(1)}m</Text>
</>
)}
</View>
<Button
title={isSensing ? "Stop Sensing" : "Start Sensing"}
onPress={() => setIsSensing(!isSensing)}
/>
<Button
title={recording ? "Stop Recording" : "Start Audio Recording"}
onPress={recording ? stopAudioRecording : startAudioRecording}
/>
<Button
title="Send Data to Cloud"
onPress={sendSensorData}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
textAlign: 'center',
},
sensorCard: {
backgroundColor: 'white',
padding: 15,
borderRadius: 10,
marginBottom: 15,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
sensorTitle: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 10,
},
});581.3 What’s Next
Now that you understand how to access smartphone sensors via Web and Native APIs, continue to:
- Participatory Sensing: Learn about crowdsourcing applications, privacy considerations, and battery optimization
- Mobile Phone Labs: Practice building mobile sensing applications with hands-on exercises
Previous: Mobile Phone Introduction | Return to: Mobile Phone as a Sensor Overview