Skip to content

Commit

Permalink
feat(sensors_plus)!: add barometer support for all platforms (#3079)
Browse files Browse the repository at this point in the history
Co-authored-by: Miguel Beltran <[email protected]>
Co-authored-by: Volodymyr Buberenko <[email protected]>
  • Loading branch information
3 people authored Jul 19, 2024
1 parent d9718cd commit 5fa797d
Show file tree
Hide file tree
Showing 18 changed files with 312 additions and 6 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ package, such as CFBundleVersion on iOS or versionCode on Android.

> [![sensors_plus][sensors_plus_badge_pub]][sensors_plus] [![pub points][sensors_plus_badge_pub_points]][sensors_plus_pub_points]
Flutter plugin for accessing accelerometer, gyroscope, and magnetometer sensors.
Flutter plugin for accessing accelerometer, gyroscope, magnetometer and barometer sensors.

[[View Source][sensors_plus_code]]

Expand Down
20 changes: 17 additions & 3 deletions packages/sensors_plus/sensors_plus/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

[<img src="../../../assets/flutter-favorite-badge.png" width="100" />](https://flutter.dev/docs/development/packages-and-plugins/favorites)

A Flutter plugin to access the accelerometer, gyroscope, and magnetometer
sensors.
A Flutter plugin to access the accelerometer, gyroscope, magnetometer and
barometer sensors.

## Platform Support

Expand Down Expand Up @@ -54,9 +54,11 @@ This will expose such classes of sensor events through a set of streams:
- `GyroscopeEvent` describes the rotation of the device.
- `MagnetometerEvent` describes the ambient magnetic field surrounding the
device. A compass is an example usage of this data.
- `BarometerEvent` describes the atmospheric pressure surrounding the device.
An altimeter is an example usage of this data. Not supported on web browsers.

These events are exposed through a `BroadcastStream`: `accelerometerEvents`,
`userAccelerometerEvents`, `gyroscopeEvents`, and `magnetometerEvents`,
`userAccelerometerEvents`, `gyroscopeEvents`, `magnetometerEvents`, and `barometerEvents`,
respectively.

> [!NOTE]
Expand Down Expand Up @@ -116,6 +118,18 @@ magnetometerEvents.listen(
cancelOnError: true,
);
// [MagnetometerEvent (x: -23.6, y: 6.2, z: -34.9)]
barometerEvents.listen(
(BarometerEvent event) {
print(event);
},
onError: (error) {
// Logic to handle error
// Needed for Android in case sensor is not available
},
cancelOnError: true,
);
// [BarometerEvent (pressure: 1000.0)]
```

Alternatively, every stream allows to specify the sampling rate for its sensor using one of predefined constants or using a custom value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ class SensorsPlugin : FlutterPlugin {
private lateinit var userAccelChannel: EventChannel
private lateinit var gyroscopeChannel: EventChannel
private lateinit var magnetometerChannel: EventChannel
private lateinit var barometerChannel: EventChannel

private lateinit var accelerometerStreamHandler: StreamHandlerImpl
private lateinit var userAccelStreamHandler: StreamHandlerImpl
private lateinit var gyroscopeStreamHandler: StreamHandlerImpl
private lateinit var magnetometerStreamHandler: StreamHandlerImpl
private lateinit var barometerStreamHandler: StreamHandlerImpl

override fun onAttachedToEngine(binding: FlutterPluginBinding) {
setupMethodChannel(binding.binaryMessenger)
Expand All @@ -41,6 +43,7 @@ class SensorsPlugin : FlutterPlugin {
"setUserAccelerometerSamplingPeriod" -> userAccelStreamHandler
"setGyroscopeSamplingPeriod" -> gyroscopeStreamHandler
"setMagnetometerSamplingPeriod" -> magnetometerStreamHandler
"setBarometerSamplingPeriod" -> barometerStreamHandler
else -> null
}
streamHandler?.samplingPeriod = call.arguments as Int
Expand Down Expand Up @@ -86,18 +89,27 @@ class SensorsPlugin : FlutterPlugin {
Sensor.TYPE_MAGNETIC_FIELD
)
magnetometerChannel.setStreamHandler(magnetometerStreamHandler)

barometerChannel = EventChannel(messenger, BAROMETER_CHANNEL_NAME)
barometerStreamHandler = StreamHandlerImpl(
sensorsManager,
Sensor.TYPE_PRESSURE
)
barometerChannel.setStreamHandler(barometerStreamHandler)
}

private fun teardownEventChannels() {
accelerometerChannel.setStreamHandler(null)
userAccelChannel.setStreamHandler(null)
gyroscopeChannel.setStreamHandler(null)
magnetometerChannel.setStreamHandler(null)
barometerChannel.setStreamHandler(null)

accelerometerStreamHandler.onCancel(null)
userAccelStreamHandler.onCancel(null)
gyroscopeStreamHandler.onCancel(null)
magnetometerStreamHandler.onCancel(null)
barometerStreamHandler.onCancel(null)
}

companion object {
Expand All @@ -111,5 +123,7 @@ class SensorsPlugin : FlutterPlugin {
"dev.fluttercommunity.plus/sensors/user_accel"
private const val MAGNETOMETER_CHANNEL_NAME =
"dev.fluttercommunity.plus/sensors/magnetometer"
private const val BAROMETER_CHANNEL_NAME =
"dev.fluttercommunity.plus/sensors/barometer"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ internal class StreamHandlerImpl(
Sensor.TYPE_LINEAR_ACCELERATION -> "User Accelerometer"
Sensor.TYPE_GYROSCOPE -> "Gyroscope"
Sensor.TYPE_MAGNETIC_FIELD -> "Magnetometer"
Sensor.TYPE_PRESSURE -> "Barometer"
else -> "Undefined"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSMotionUsageDescription</key>
<string>This app requires access to motion data as an example for the sensors plugin.</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
Expand Down
64 changes: 63 additions & 1 deletion packages/sensors_plus/sensors_plus/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,19 @@ class _MyHomePageState extends State<MyHomePage> {
AccelerometerEvent? _accelerometerEvent;
GyroscopeEvent? _gyroscopeEvent;
MagnetometerEvent? _magnetometerEvent;
BarometerEvent? _barometerEvent;

DateTime? _userAccelerometerUpdateTime;
DateTime? _accelerometerUpdateTime;
DateTime? _gyroscopeUpdateTime;
DateTime? _magnetometerUpdateTime;
DateTime? _barometerUpdateTime;

int? _userAccelerometerLastInterval;
int? _accelerometerLastInterval;
int? _gyroscopeLastInterval;
int? _magnetometerLastInterval;
int? _barometerLastInterval;
final _streamSubscriptions = <StreamSubscription<dynamic>>[];

Duration sensorInterval = SensorInterval.normalInterval;
Expand Down Expand Up @@ -101,7 +104,7 @@ class _MyHomePageState extends State<MyHomePage> {
),
),
Padding(
padding: const EdgeInsets.all(20.0),
padding: const EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 0.0),
child: Table(
columnWidths: const {
0: FlexColumnWidth(4),
Expand Down Expand Up @@ -169,6 +172,35 @@ class _MyHomePageState extends State<MyHomePage> {
],
),
),
Padding(
padding: const EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 20.0),
child: Table(
columnWidths: const {
0: FlexColumnWidth(4),
1: FlexColumnWidth(3),
2: FlexColumnWidth(2),
},
children: [
const TableRow(
children: [
SizedBox.shrink(),
Text('Pressure'),
Text('Interval'),
],
),
TableRow(
children: [
const Padding(
padding: EdgeInsets.symmetric(vertical: 8.0),
child: Text('Barometer'),
),
Text(_barometerEvent?.pressure.toStringAsFixed(1) ?? '?'),
Text('${_barometerLastInterval?.toString() ?? '?'} ms'),
],
),
],
),
),
Column(
mainAxisSize: MainAxisSize.min,
children: [
Expand Down Expand Up @@ -209,6 +241,7 @@ class _MyHomePageState extends State<MyHomePage> {
accelerometerEventStream(samplingPeriod: sensorInterval);
gyroscopeEventStream(samplingPeriod: sensorInterval);
magnetometerEventStream(samplingPeriod: sensorInterval);
barometerEventStream(samplingPeriod: sensorInterval);
});
},
),
Expand Down Expand Up @@ -346,5 +379,34 @@ class _MyHomePageState extends State<MyHomePage> {
cancelOnError: true,
),
);
_streamSubscriptions.add(
barometerEventStream(samplingPeriod: sensorInterval).listen(
(BarometerEvent event) {
final now = DateTime.now();
setState(() {
_barometerEvent = event;
if (_barometerUpdateTime != null) {
final interval = now.difference(_barometerUpdateTime!);
if (interval > _ignoreDuration) {
_barometerLastInterval = interval.inMilliseconds;
}
}
});
_barometerUpdateTime = now;
},
onError: (e) {
showDialog(
context: context,
builder: (context) {
return const AlertDialog(
title: Text("Sensor Not Found"),
content: Text(
"It seems that your device doesn't support Barometer Sensor"),
);
});
},
cancelOnError: true,
),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ public class FPPSensorsPlusPlugin: NSObject, FlutterPlugin {
_eventChannels[magnetometerStreamHandlerName] = magnetometerChannel
_streamHandlers[magnetometerStreamHandlerName] = magnetometerStreamHandler

let barometerStreamHandler = FPPBarometerStreamHandlerPlus()
let barometerStreamHandlerName = "dev.fluttercommunity.plus/sensors/barometer"
let barometerChannel = FlutterEventChannel(
name: barometerStreamHandlerName,
binaryMessenger: registrar.messenger()
)
barometerChannel.setStreamHandler(barometerStreamHandler)
_eventChannels[barometerStreamHandlerName] = barometerChannel
_streamHandlers[barometerStreamHandlerName] = barometerStreamHandler

let methodChannel = FlutterMethodChannel(
name: "dev.fluttercommunity.plus/sensors/method",
binaryMessenger: registrar.messenger()
Expand All @@ -66,6 +76,8 @@ public class FPPSensorsPlusPlugin: NSObject, FlutterPlugin {
streamHandler = _streamHandlers[gyroscopeStreamHandlerName]
case "setMagnetometerSamplingPeriod":
streamHandler = _streamHandlers[magnetometerStreamHandlerName]
case "setBarometerSamplingPeriod":
streamHandler = _streamHandlers[barometerStreamHandlerName]
default:
return result(FlutterMethodNotImplemented)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import CoreMotion

let GRAVITY = 9.81
var _motionManager: CMMotionManager!
var _altimeter: CMAltimeter!

public protocol MotionStreamHandler: FlutterStreamHandler {
var samplingPeriod: Int { get set }
Expand All @@ -24,6 +25,12 @@ func _initMotionManager() {
}
}

func _initAltimeter() {
if (_altimeter == nil) {
_altimeter = CMAltimeter()
}
}

func sendTriplet(x: Float64, y: Float64, z: Float64, sink: @escaping FlutterEventSink) {
if _isCleanUp {
return
Expand Down Expand Up @@ -219,3 +226,59 @@ class FPPMagnetometerStreamHandlerPlus: NSObject, MotionStreamHandler {
FPPSensorsPlusPlugin._cleanUp()
}
}

class FPPBarometerStreamHandlerPlus: NSObject, MotionStreamHandler {

var samplingPeriod = 200000 {
didSet {
_initAltimeter()
// Note: CMAltimeter does not provide a way to set the sampling period directly.
// The sampling period would typically be managed by starting/stopping the updates.
}
}

func onListen(
withArguments arguments: Any?,
eventSink sink: @escaping FlutterEventSink
) -> FlutterError? {
_initAltimeter()
if CMAltimeter.isRelativeAltitudeAvailable() {
_altimeter.startRelativeAltitudeUpdates(to: OperationQueue()) { data, error in
if _isCleanUp {
return
}
if (error != nil) {
sink(FlutterError(
code: "UNAVAILABLE",
message: error!.localizedDescription,
details: nil
))
return
}
let pressure = data!.pressure.doubleValue * 10.0 // kPa to hPa (hectopascals)
DispatchQueue.main.async {
let pressureArray: [Double] = [pressure]
pressureArray.withUnsafeBufferPointer { buffer in
sink(FlutterStandardTypedData.init(float64: Data(buffer: buffer)))
}
}
}
} else {
return FlutterError(
code: "UNAVAILABLE",
message: "Barometer is not available on this device",
details: nil
)
}
return nil
}

func onCancel(withArguments arguments: Any?) -> FlutterError? {
_altimeter.stopRelativeAltitudeUpdates()
return nil
}

func dealloc() {
FPPSensorsPlusPlugin._cleanUp()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Pod::Spec.new do |s|
s.version = '0.0.1'
s.summary = 'Flutter Sensors'
s.description = <<-DESC
Flutter plugin to access the accelerometer, gyroscope, and magnetometer sensors.
Flutter plugin to access the accelerometer, gyroscope, magnetometer and barometer sensors.
DESC
s.homepage = 'https:/fluttercommunity/plus_plugins'
s.license = { :type => 'BSD', :file => '../LICENSE' }
Expand Down
9 changes: 9 additions & 0 deletions packages/sensors_plus/sensors_plus/lib/sensors_plus.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,12 @@ Stream<MagnetometerEvent> magnetometerEventStream({
}) {
return _sensors.magnetometerEventStream(samplingPeriod: samplingPeriod);
}

/// Returns a broadcast stream of events from the device barometer at the
/// given sampling frequency.
@override
Stream<BarometerEvent> barometerEventStream({
Duration samplingPeriod = SensorInterval.normalInterval,
}) {
return _sensors.barometerEventStream(samplingPeriod: samplingPeriod);
}
13 changes: 13 additions & 0 deletions packages/sensors_plus/sensors_plus/lib/src/sensors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,17 @@ class Sensors extends SensorsPlatform {
}) {
return _platform.magnetometerEventStream(samplingPeriod: samplingPeriod);
}

/// Returns a broadcast stream of events from the device barometer at the
/// given sampling frequency.
///
/// This method always returning the same stream. If this method is called
/// again, the sampling period of the stream will be update. All previous
/// listener will also be affected.
@override
Stream<BarometerEvent> barometerEventStream({
Duration samplingPeriod = SensorInterval.normalInterval,
}) {
return _platform.barometerEventStream(samplingPeriod: samplingPeriod);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,12 @@ Stream<MagnetometerEvent> magnetometerEventStream({
}) {
return _sensors.magnetometerEventStream(samplingPeriod: samplingPeriod);
}

/// Returns a broadcast stream of events from the device barometer at the
/// given sampling frequency.
@override
Stream<BarometerEvent> barometerEventStream({
Duration samplingPeriod = SensorInterval.normalInterval,
}) {
return _sensors.barometerEventStream(samplingPeriod: samplingPeriod);
}
Loading

0 comments on commit 5fa797d

Please sign in to comment.