Requirement Description
Developers frequently use various sensors in different games and applications. In particular, the gyroscope sensor allows for great balance without being affected by gravity, enabling the creation of fun and engaging interfaces for users. By using the accelerometer and gyroscope sensors simultaneously, various systems such as aiming mechanisms, balance ball simulations, and air mouse functionality can be implemented.
Background Knowledge
Thanks to the Sensor Service Kit included in HarmonyOS, developers can not only access data from many different sensors but also use these sensors to create a variety of applications. Some of these sensors are as follows:
- Accelerometer
- Gravity
- Gyroscope
- Barometer
- Heart Rate
- Ambition
You can review the documentation for all sensor data provided by the relevant kit.
Implementation Steps
In the following example, it is explained how a balance simulation can be developed for a wearable device using gyroscope and accelerometer sensor data. By implementing the examples in this section, a dynamic balance interface–based application can be created. The implementation sequence and the corresponding algorithm are as follows:
-
Define Component and States
- Create a component
Index. - Initialize state variables for accelerometer (
accelX,accelY,accelZ) and gyroscope (gyroX,gyroY,gyroZ). - Initialize state variables for cursor position (
cursorX,cursorY) and circle color (circleColor). - Set private variables for circle dimensions, cursor dot size, sensitivity, and edge threshold.
- Create a component
-
Calculate Circle Dimensions on Component Mount
- In
aboutToAppear(): - Get the display width.
- Calculate circle diameter, radius, and center coordinates.
- Start sensor monitoring.
- In
-
Stop Sensors on Component Unmount
- In
aboutToDisappear(), stop the accelerometer and gyroscope sensors.
- In
-
Start Sensor Monitoring
- Use
sensor.on()for accelerometer and gyroscope. - Update state variables with sensor readings.
- Call
updateCursorPosition()with accelerometer data. - Show a toast message indicating sensors have started.
- Handle any errors using
BusinessErrorand display an error toast.
- Use
-
Update Cursor Position
- Calculate the new cursor position based on accelerometer values and sensitivity.
- Calculate distance from the circle center.
- If the cursor exceeds the circle boundary, adjust the position to stay within the circle using trigonometry.
- Call
checkEdgeProximity()to update circle color if near the edge. - Update state variables
cursorXandcursorY.
-
Check Edge Proximity
- Calculate distance from cursor to the edge.
- Change circle color to blue if the cursor is near the edge, otherwise keep it red.
-
Stop Sensor Monitoring
- Use
sensor.off()for accelerometer and gyroscope. - Set
isMonitoringto false. - Show a toast message indicating sensors have stopped.
- Handle errors using
BusinessError.
- Use
-
Show Cursor Position
- Display a toast with current cursor coordinates when the user taps the screen.
-
Build UI
- Create a
Stackcontainer: - Draw the circle with current
circleColor. - Draw the cursor dot at (
cursorX,cursorY) with opacity. - Set full width and height for the container.
- Add background color.
- Attach an
onClickhandler to show the cursor position.
- Create a
Code Snippet / Configuration
The following code block provides all the implementation steps necessary for completing the respective project.
import { sensor } from '@kit.SensorServiceKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { promptAction } from '@kit.ArkUI';
import display from '@ohos.display';
@Entry
@Component
struct Index {
@State accelX: string = '0.000';
@State accelY: string = '0.000';
@State accelZ: string = '0.000';
@State gyroX: string = '0.000';
@State gyroY: string = '0.000';
@State gyroZ: string = '0.000';
@State isMonitoring: boolean = false;
@State cursorX: number = 111.5;
@State cursorY: number = 111.5;
@State circleColor: Color = Color.Red;
private circleDiameter: number = 0
private circleRadius: number = 0
private circleCenterX: number = 0
private circleCenterY: number = 0
private dotRadius: number = 7.5;
private sensitivity: number = 2;
private edgeThreshold: number = 10;
aboutToAppear(): void {
this.circleDiameter = display.getDefaultDisplaySync().width/2
this.circleRadius = this.circleDiameter / 2;
this.circleCenterX = this.circleRadius;
this.circleCenterY = this.circleRadius;
this.startSensors()
}
aboutToDisappear(): void {
this.stopSensors()
}
private startSensors(): void {
try {
sensor.on(sensor.SensorId.ACCELEROMETER, (data: sensor.AccelerometerResponse) => {
this.accelX = data.x.toFixed(3);
this.accelY = data.y.toFixed(3);
this.accelZ = data.z.toFixed(3);
this.updateCursorPosition(data.x, data.y);
}, { interval: 10000000 });
sensor.on(sensor.SensorId.GYROSCOPE, (data: sensor.GyroscopeResponse) => {
this.gyroX = data.x.toFixed(3);
this.gyroY = data.y.toFixed(3);
this.gyroZ = data.z.toFixed(3);
}, { interval: 10000000 });
this.isMonitoring = true;
promptAction.showToast({ message: 'Sensors started', duration: 1000 });
} catch (error) {
const e: BusinessError = error as BusinessError;
console.error(`Failed to start sensors. Code: ${e.code}, message: ${e.message}`);
promptAction.showToast({ message: `Error: ${e.code}`, duration: 2000 });
}
}
private updateCursorPosition(accelX: number, accelY: number): void {
let newX = this.cursorX + (-accelX * this.sensitivity);
let newY = this.cursorY + (accelY * this.sensitivity);
const distanceFromCenter = Math.sqrt(
Math.pow(newX + this.dotRadius - this.circleCenterX, 2) +
Math.pow(newY + this.dotRadius - this.circleCenterY, 2)
);
if (distanceFromCenter > this.circleRadius - this.dotRadius) {
const angle = Math.atan2(
newY + this.dotRadius - this.circleCenterY,
newX + this.dotRadius - this.circleCenterX
);
newX = this.circleCenterX + Math.cos(angle) * (this.circleRadius - this.dotRadius) - this.dotRadius;
newY = this.circleCenterY + Math.sin(angle) * (this.circleRadius - this.dotRadius) - this.dotRadius;
}
this.checkEdgeProximity(distanceFromCenter);
this.cursorX = newX;
this.cursorY = newY;
}
private checkEdgeProximity(distanceFromCenter: number): void {
const distanceToEdge = this.circleRadius - distanceFromCenter;
if (distanceToEdge <= this.edgeThreshold) {
this.circleColor = Color.Blue;
} else {
this.circleColor = Color.Red;
}
}
private stopSensors(): void {
try {
sensor.off(sensor.SensorId.ACCELEROMETER);
sensor.off(sensor.SensorId.GYROSCOPE);
this.isMonitoring = false;
promptAction.showToast({ message: 'Sensors stopped', duration: 1000 });
} catch (error) {
const e: BusinessError = error as BusinessError;
console.error(`Failed to stop sensors. Code: ${e.code}, message: ${e.message}`);
}
}
private showPosition(): void {
const positionText = `X: ${this.cursorX.toFixed(1)}, Y: ${this.cursorY.toFixed(1)}`;
promptAction.showToast({
message: positionText,
duration: 1000,
bottom: '50%'
});
}
build() {
Stack() {
Circle()
.width(this.circleDiameter)
.height(this.circleDiameter)
.fill(Color.Transparent)
.stroke(this.circleColor)
.strokeWidth(3)
Circle()
.width(15)
.height(15)
.position({ x: this.cursorX, y: this.cursorY })
.fill(Color.Red)
.opacity(0.8)
}
.width('100%')
.height('100%')
.backgroundColor('#1a1a1a')
.onClick(() => {
this.showPosition();
})
}
}
In addition, developers must also ensure the required sensor permissions.
"requestPermissions": [
{
"name": "ohos.permission.ACCELEROMETER",
"reason": "$string:app_name",
"usedScene": {
}
},
{
"name": "ohos.permission.GYROSCOPE",
"reason": "$string:app_name",
"usedScene": {
}
}
]
Test Results
You can view sample screenshots from the application.
Limitation and Considerations
A real HarmonyOS watch is required to test the relevant sensor data, as the sensors do not work on emulators.
Related Documents or Links
https://developer.huawei.com/consumer/en/doc/harmonyos-references/js-apis-sensor



Top comments (0)