22// Copyright (c) .NET Foundation and Contributors
33// See LICENSE file in the project root for full license information.
44//
5+ using nanoFramework . Device . Bluetooth . Advertisement ;
56using System ;
7+ using System . Collections ;
8+ using System . Threading ;
69
710namespace nanoFramework . Device . Bluetooth
811{
@@ -16,38 +19,188 @@ public class BluetoothSignalStrengthFilter
1619 private short _outOfRangeThresholdInDBm ;
1720 private TimeSpan _outOfRangeTimeout ;
1821
22+ private Timer _scanCheck ;
23+ private Hashtable _scanResults = new ( ) ;
24+ private Object _scanResultsLock = new Object ( ) ;
25+
26+ private const int DefaultDBM = - 127 ;
27+ private const int DefaultOorTimeout = 60 ;
28+
29+ private class ScanItem
30+ {
31+ public short Rssi ; // RSSI of last scan
32+ public bool InRange ; // True if device was in range.
33+ public DateTime OutRangeTime ; // Time device went out of range
34+ public bool Active ; // Scan item Active, used for purging entries
35+ }
36+
1937 /// <summary>
2038 /// Create a new BluetoothSignalStrengthFilter object.
2139 /// </summary>
2240 public BluetoothSignalStrengthFilter ( )
2341 {
24- InRangeThresholdInDBm = - 128 ;
25- OutOfRangeThresholdInDBm = - 128 ;
26- OutOfRangeTimeout = new TimeSpan ( 0 , 0 , 10 ) ; // seconds
42+ InRangeThresholdInDBm = DefaultDBM ;
43+ OutOfRangeThresholdInDBm = DefaultDBM ;
44+ OutOfRangeTimeout = new TimeSpan ( 0 , 0 , DefaultOorTimeout ) ; // seconds
45+
46+ _scanResults = new Hashtable ( ) ;
47+
48+ // Remove inactive entries from scan results every 1 minute
49+ TimeSpan time = new TimeSpan ( 0 , 1 , 0 ) ;
50+ _scanCheck = new Timer ( ScanCheckCallback , 0 , time , time ) ;
2751 }
2852
2953 ///// <summary>
30- ///// The interval at which received signal strength indicator (RSSI) events are sampled.
54+ ///// The interval at which received signal strength indicator (RSSI) events are sampled. (TODO)
3155 ///// </summary>
3256 //public TimeSpan? SamplingInterval { get; set; }
3357
3458 /// <summary>
3559 /// The time out for a received signal strength indicator (RSSI) event to be considered
36- /// out of range.
60+ /// out of range. Value between 1 and 60 seconds.
3761 /// </summary>
3862 public TimeSpan OutOfRangeTimeout { get => _outOfRangeTimeout ; set => _outOfRangeTimeout = value ; }
3963
4064 /// <summary>
4165 /// The minimum received signal strength indicator (RSSI) value in dBm on which RSSI
42- /// events will be considered out of range.
66+ /// events will be considered out of range. Value between +20 and -127. Default vale is -127.
4367 /// </summary>
4468 public short OutOfRangeThresholdInDBm { get => _outOfRangeThresholdInDBm ; set => _outOfRangeThresholdInDBm = value ; }
4569
4670 /// <summary>
4771 /// The minimum received signal strength indicator (RSSI) value in dBm on which RSSI
4872 /// events will be propagated or considered in range if the previous events were
49- /// considered out of range.
73+ /// considered out of range. Value between +20 and -127. Default vale is -127.
5074 /// </summary>
5175 public short InRangeThresholdInDBm { get => _inRangeThresholdInDBm ; set => _inRangeThresholdInDBm = value ; }
76+
77+ /// <summary>
78+ /// Signal strength filter (RSSI).
79+ /// </summary>
80+ /// <param name="args">BluetoothLEAdvertisementReceivedEventArgs</param>
81+ /// <returns>Returns False to ignore event</returns>
82+ internal bool Filter ( BluetoothLEAdvertisementReceivedEventArgs args )
83+ {
84+ // If default setting then just accept all events
85+ if ( InRangeThresholdInDBm == DefaultDBM )
86+ {
87+ return true ;
88+ }
89+
90+ ScanItem scan = FindScanEntry ( args . BluetoothAddress ) ;
91+ bool inRange = ( args . RawSignalStrengthInDBm >= InRangeThresholdInDBm ) ;
92+ if ( scan == null && inRange )
93+ {
94+ // New entry and in range then add it to list
95+ scan = AddOrReplaceScanEntry ( args . BluetoothAddress , args . RawSignalStrengthInDBm , inRange ) ;
96+ }
97+
98+ if ( scan != null )
99+ {
100+ scan . Active = true ; // flag entry as active so not purged
101+
102+ if ( ! inRange )
103+ {
104+ if ( args . RawSignalStrengthInDBm < OutOfRangeThresholdInDBm )
105+ {
106+ // Completely out of range, ignore
107+ DeleteScanEntry ( args . BluetoothAddress ) ;
108+ return false ;
109+ }
110+
111+ // In between in and out of range thresholds, check time out of range
112+ if ( scan . InRange )
113+ {
114+ // If previously in range and now out
115+ // then set date time for time out of range
116+ AddOrReplaceScanEntry ( args . BluetoothAddress , args . RawSignalStrengthInDBm , inRange ) ;
117+ }
118+ else
119+ {
120+ // If previously moved out of range then check timer and still out of range.
121+ if ( ( scan . OutRangeTime + OutOfRangeTimeout ) < DateTime . UtcNow )
122+ {
123+ // Moved out of range for time out period
124+ // Remove scan
125+ DeleteScanEntry ( args . BluetoothAddress ) ;
126+ return false ;
127+ }
128+ }
129+ }
130+
131+ return true ;
132+ }
133+ return false ;
134+ }
135+
136+ /// <summary>
137+ /// Called every 5 minutes to purge scan hash.
138+ /// </summary>
139+ /// <param name="state"></param>
140+ private void ScanCheckCallback ( object state )
141+ {
142+ lock ( _scanResultsLock )
143+ {
144+ ArrayList removeList = new ArrayList ( ) ;
145+
146+ foreach ( DictionaryEntry item in _scanResults )
147+ {
148+ if ( ! ( ( ScanItem ) item . Value ) . Active )
149+ {
150+ removeList . Add ( item . Key ) ;
151+ }
152+ else
153+ {
154+ ( ( ScanItem ) item . Value ) . Active = false ;
155+ }
156+ }
157+
158+ // Now delete expired items
159+ foreach ( ulong addressKey in removeList )
160+ {
161+ DeleteScanEntry ( addressKey ) ;
162+ }
163+ }
164+ }
165+
166+ private ScanItem FindScanEntry ( UInt64 address )
167+ {
168+ if ( _scanResults . Contains ( address ) )
169+ {
170+ return ( ScanItem ) _scanResults [ address ] ;
171+ }
172+
173+ return null ;
174+ }
175+
176+ private ScanItem AddOrReplaceScanEntry ( UInt64 address , short rssi , bool inRange )
177+ {
178+ ScanItem item = new ( )
179+ {
180+ Rssi = rssi ,
181+ InRange = inRange ,
182+ Active = true
183+ } ;
184+
185+ if ( ! inRange )
186+ {
187+ // It out of range now, save time for time out checking
188+ item . OutRangeTime = DateTime . UtcNow ;
189+ }
190+
191+ DeleteScanEntry ( address ) ;
192+
193+ _scanResults . Add ( address , item ) ;
194+
195+ return item ;
196+ }
197+
198+ private void DeleteScanEntry ( UInt64 address )
199+ {
200+ if ( _scanResults . Contains ( address ) )
201+ {
202+ _scanResults . Remove ( address ) ;
203+ }
204+ }
52205 }
53206}
0 commit comments