1+ /*
2+ Battery Monitor
3+
4+ This example creates a BLE peripheral with the standard battery service and
5+ level characteristic. The A0 pin is used to calculate the battery level.
6+
7+ The circuit:
8+ - Arduino MKR WiFi 1010, Arduino Uno WiFi Rev2 board, Arduino Nano 33 IoT,
9+ Arduino Nano 33 BLE, or Arduino Nano 33 BLE Sense board.
10+
11+ You can use a generic BLE central app, like LightBlue (iOS and Android) or
12+ nRF Connect (Android), to interact with the services and characteristics
13+ created in this sketch.
14+
15+ This example code is in the public domain.
16+ */
17+
18+ #include < ArduinoBLE.h>
19+
20+
21+ #define PAIR_BUTTON 3 // button for pairing
22+ #define PAIR_LED 24 // LED used to signal pairing
23+ #define PAIR_LED_ON LOW // Blue LED on Nano BLE has inverted logic
24+ #define PAIR_INTERVAL 30000 // interval for pairing after button press in ms
25+
26+ #define CTRL_LED LED_BUILTIN
27+
28+
29+ // BLE Battery Service
30+ BLEService batteryService (" 180F" );
31+
32+ // BLE Battery Level Characteristic
33+ BLEUnsignedCharCharacteristic batteryLevelChar (" 2A19" , // standard 16-bit characteristic UUID
34+ BLERead | BLENotify); // remote clients will be able to get notifications if this characteristic changes
35+ BLEStringCharacteristic stringcharacteristic (" 183E" , BLERead | BLEWrite, 31 );
36+
37+
38+ // Add BLEEncryption tag to require pairing. This controls the LED.
39+ BLEUnsignedCharCharacteristic secretValue (" 2a3F" , BLERead | BLEWrite | BLEEncryption);
40+
41+ int oldBatteryLevel = 0 ; // last battery level reading from analog input
42+ unsigned long previousMillis = 0 ; // last time the battery level was checked, in ms
43+ unsigned long pairingStarted = 0 ; // pairing start time when button is pressed
44+ bool wasConnected = 0 ;
45+ bool acceptOrReject = true ;
46+
47+ void setup () {
48+ Serial.begin (9600 ); // initialize serial communication
49+ while (!Serial);
50+
51+ pinMode (CTRL_LED, OUTPUT); // initialize the built-in LED pin to indicate when a central is connected
52+ pinMode (PAIR_LED, OUTPUT);
53+ pinMode (PAIR_BUTTON, INPUT_PULLUP);
54+
55+
56+ Serial.println (" Serial connected" );
57+
58+ // Callback function with confirmation code when new device is pairing.
59+ BLE.setDisplayCode ([](uint32_t confirmCode){
60+ Serial.println (" New device pairing request." );
61+ Serial.print (" Confirm code matches pairing device: " );
62+ char code[6 ];
63+ sprintf (code, " %06d" , confirmCode);
64+ Serial.println (code);
65+ });
66+
67+ // Callback to allow accepting or rejecting pairing
68+ BLE.setBinaryConfirmPairing ([&acceptOrReject](){
69+ Serial.print (" Should we confirm pairing? " );
70+ delay (5000 );
71+ if (acceptOrReject){
72+ acceptOrReject = false ;
73+ Serial.println (" yes" );
74+ return true ;
75+ }else {
76+ acceptOrReject = true ;
77+ Serial.println (" no" );
78+ return false ;
79+ }
80+ });
81+
82+ // IRKs are keys that identify the true owner of a random mac address.
83+ // Add IRKs of devices you are bonded with.
84+ BLE.setGetIRKs ([](uint8_t * nIRKs, uint8_t ** BDaddrTypes, uint8_t *** BDAddrs, uint8_t *** IRKs){
85+ // Set to number of devices
86+ *nIRKs = 2 ;
87+
88+ *BDAddrs = new uint8_t *[*nIRKs];
89+ *IRKs = new uint8_t *[*nIRKs];
90+ *BDaddrTypes = new uint8_t [*nIRKs];
91+
92+ // Set these to the mac and IRK for your bonded devices as printed in the serial console after bonding.
93+ uint8_t device1Mac[6 ] = {0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 };
94+ uint8_t device1IRK[16 ] = {0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 };
95+
96+ uint8_t device2Mac[6 ] = {0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 };
97+ uint8_t device2IRK[16 ] = {0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 };
98+
99+
100+ (*BDaddrTypes)[0 ] = 0 ; // Type 0 is for pubc address, type 1 is for static random
101+ (*BDAddrs)[0 ] = new uint8_t [6 ];
102+ (*IRKs)[0 ] = new uint8_t [16 ];
103+ memcpy ((*IRKs)[0 ] , device1IRK,16 );
104+ memcpy ((*BDAddrs)[0 ], device1Mac, 6 );
105+
106+
107+ (*BDaddrTypes)[1 ] = 0 ;
108+ (*BDAddrs)[1 ] = new uint8_t [6 ];
109+ (*IRKs)[1 ] = new uint8_t [16 ];
110+ memcpy ((*IRKs)[1 ] , device2IRK,16 );
111+ memcpy ((*BDAddrs)[1 ], device2Mac, 6 );
112+
113+
114+ return 1 ;
115+ });
116+ // The LTK is the secret key which is used to encrypt bluetooth traffic
117+ BLE.setGetLTK ([](uint8_t * address, uint8_t * LTK){
118+ // address is input
119+ Serial.print (" Received request for address: " );
120+ btct.printBytes (address,6 );
121+
122+ // Set these to the MAC and LTK of your devices after bonding.
123+ uint8_t device1Mac[6 ] = {0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 };
124+ uint8_t device1LTK[16 ] = {0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 };
125+ uint8_t device2Mac[6 ] = {0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 };
126+ uint8_t device2LTK[16 ] = {0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 };
127+
128+
129+ if (memcmp (device1Mac, address, 6 ) == 0 ) {
130+ memcpy (LTK, device1LTK, 16 );
131+ return 1 ;
132+ }else if (memcmp (device2Mac, address, 6 ) == 0 ) {
133+ memcpy (LTK, device2LTK, 16 );
134+ return 1 ;
135+ }
136+ return 0 ;
137+ });
138+ BLE.setStoreIRK ([](uint8_t * address, uint8_t * IRK){
139+ Serial.print (F (" New device with MAC : " ));
140+ btct.printBytes (address,6 );
141+ Serial.print (F (" Need to store IRK : " ));
142+ btct.printBytes (IRK,16 );
143+ return 1 ;
144+ });
145+ BLE.setStoreLTK ([](uint8_t * address, uint8_t * LTK){
146+ Serial.print (F (" New device with MAC : " ));
147+ btct.printBytes (address,6 );
148+ Serial.print (F (" Need to store LTK : " ));
149+ btct.printBytes (LTK,16 );
150+ return 1 ;
151+ });
152+
153+ while (1 ){
154+ // begin initialization
155+ if (!BLE.begin ()) {
156+ Serial.println (" starting BLE failed!" );
157+ delay (200 );
158+ continue ;
159+ }
160+ Serial.println (" BT init" );
161+ delay (200 );
162+
163+ /* Set a local name for the BLE device
164+ This name will appear in advertising packets
165+ and can be used by remote devices to identify this BLE device
166+ The name can be changed but maybe be truncated based on space left in advertisement packet
167+ */
168+
169+ BLE.setDeviceName (" Arduino" );
170+ BLE.setLocalName (" BatteryMonitor" );
171+
172+ BLE.setAdvertisedService (batteryService); // add the service UUID
173+ batteryService.addCharacteristic (batteryLevelChar); // add the battery level characteristic
174+ batteryService.addCharacteristic (stringcharacteristic);
175+ batteryService.addCharacteristic (secretValue);
176+
177+ BLE.addService (batteryService); // Add the battery service
178+ batteryLevelChar.writeValue (oldBatteryLevel); // set initial value for this characteristic
179+ char * stringCharValue = new char [32 ];
180+ stringCharValue = " string" ;
181+ stringcharacteristic.writeValue (stringCharValue);
182+ secretValue.writeValue (0 );
183+
184+ delay (1000 );
185+
186+ // prevent pairing until button is pressed (will show a pairing rejected message)
187+ BLE.setPairable (false );
188+
189+ /* Start advertising BLE. It will start continuously transmitting BLE
190+ advertising packets and will be visible to remote BLE central devices
191+ until it receives a new connection */
192+
193+ // start advertising
194+ if (!BLE.advertise ()){
195+ Serial.println (" failed to advertise bluetooth." );
196+ BLE.stopAdvertise ();
197+ delay (500 );
198+ }else {
199+ Serial.println (" advertising..." );
200+ break ;
201+ }
202+ BLE.end ();
203+ delay (100 );
204+ }
205+ }
206+
207+
208+ void loop () {
209+ // wait for a BLE central
210+ BLEDevice central = BLE.central ();
211+
212+
213+ // If button is pressed, allow pairing for 30 sec
214+ if (!BLE.pairable () && digitalRead (PAIR_BUTTON) == LOW){
215+ pairingStarted = millis ();
216+ BLE.setPairable (Pairable::ONCE);
217+ Serial.println (" Accepting pairing for 30s" );
218+ } else if (BLE.pairable () && millis () > pairingStarted + PAIR_INTERVAL){
219+ BLE.setPairable (false );
220+ Serial.println (" No longer accepting pairing" );
221+ }
222+ // Make LED blink while pairing is allowed
223+ digitalWrite (PAIR_LED, (BLE.pairable () ? (millis ()%400 )<200 : BLE.paired ()) ? PAIR_LED_ON : !PAIR_LED_ON);
224+
225+
226+ // if a central is connected to the peripheral:
227+ if (central && central.connected ()) {
228+ if (!wasConnected){
229+ wasConnected = true ;
230+ Serial.print (" Connected to central: " );
231+ // print the central's BT address:
232+ Serial.println (central.address ());
233+ }
234+
235+ // check the battery level every 200ms
236+ // while the central is connected:
237+ long currentMillis = millis ();
238+ // if 200ms have passed, check the battery level:
239+ if (currentMillis - previousMillis >= 1000 ) {
240+ previousMillis = currentMillis;
241+ updateBatteryLevel ();
242+ digitalWrite (CTRL_LED, secretValue.value ()>0 ? HIGH : LOW);
243+ }
244+ } else if (wasConnected){
245+ wasConnected = false ;
246+ Serial.print (" Disconnected from central: " );
247+ Serial.println (central.address ());
248+ }
249+
250+ }
251+
252+ void updateBatteryLevel () {
253+ /* Read the current voltage level on the A0 analog input pin.
254+ This is used here to simulate the charge level of a battery.
255+ */
256+ int battery = analogRead (A0);
257+ int batteryLevel = map (battery, 0 , 1023 , 0 , 100 );
258+
259+ if (batteryLevel != oldBatteryLevel) { // if the battery level has changed
260+ // Serial.print("Battery Level % is now: "); // print it
261+ // Serial.println(batteryLevel);
262+ batteryLevelChar.writeValue (batteryLevel); // and update the battery level characteristic
263+ oldBatteryLevel = batteryLevel; // save the level for next comparison
264+ }
265+ }
0 commit comments