5. Input & Output device¶
This week we continued working on electronics, focusing more on the hardware and sensors and trying to figure out which ones are most suitable for our project. We then split up with each person being given a component to try out and then document how its used and how to tackle any difficulties.
Using ESP32¶
At first we tried out the ESP32 based microcontroller, this time focusing on the different types of signals as indicated in the following figure.
This image even explains the reasoning behind the colors of each component we used with the microcontroller, for example, red components are digital and must be programmed with the red colored pin number of the port they are inserted in.
I tested out the following devices. All inserted into port number 4:
Buzzer¶
Used the following code to play a simple tune. I could have tried to make a more complex tune to resemble a song for example, but I thought the class had enough buzzer going off at the same time.
from machine import Pin
import time
Buzzer = Pin(2,Pin.OUT)
while True:
Buzzer.value(1)
time.sleep(1)
Buzzer.value(0)
time.sleep(0.5)
Buzzer.value(1)
time.sleep(1)
Buzzer.value(0)
time.sleep(0.5)
Buzzer.value(1)
time.sleep(1)
Buzzer.value(0)
time.sleep(0.5)
NeoPixel RGB LED¶
Used this code to test out how to use NeoPixel RGB Devices which only use 1 pin number.
import machine, neopixel
np = neopixel.NeoPixel(machine.Pin(27), 8)
np[0] = (255, 0, 0) # set to red, full brightness
np[1] = (0, 128, 0) # set to green, half brightness
np[2] = (0, 0, 64) # set to blue, quarter brightness
np[3] = (0, 0, 0) # alone
import time
def demo(np):
n = np.n
# cycle
for i in range(4 * n):
for j in range(n):
np[j] = (0, 0, 0)
np[i % n] = (255, 255, 255)
np.write()
time.sleep_ms(25)
# bounce
for i in range(4 * n):
for j in range(n):
np[j] = (0, 0, 128)
if (i // n) % 2 == 0:
np[i % n] = (0, 0, 0)
else:
np[n - 1 - (i % n)] = (0, 0, 0)
np.write()
time.sleep_ms(60)
# fade in/out
for i in range(0, 4 * 256, 8):
for j in range(n):
if (i // 256) % 2 == 0:
val = i & 0xff
else:
val = 255 - (i & 0xff)
np[j] = (val, 0, 0)
np.write()
# clear
for i in range(n):
np[i] = (0, 0, 0)
np.write()
demo(np)
This is possible because of the NeoPixel Library.
Humidity Sensor¶
Used this code to operate the sensor and print out the readings. Note that if you use DHT22 instead of DHT 11 you will get very high and incorrect readings.
# Complete project details at https://RandomNerdTutorials.com
from machine import Pin
from time import sleep
import dht
sensor = dht.DHT11(Pin(27))
#sensor = dht.DHT22(Pin(14))
# The one we have for the esp32 is DHT11, if you choose DHT22 you will get wrong values.
while True:
try:
sleep(0.5)
sensor.measure()
temp = sensor.temperature()
hum = sensor.humidity()
temp_f = temp * (9/5) + 32.0
print('Temperature: %3.1f C' %temp)
print('Temperature: %3.1f F' %temp_f)
print('Humidity: %3.1f %%' %hum)
except OSError as e:
print('Failed to read sensor.')
Potentiometer¶
Used this code to print out the current values of the potentiometer.
# Complete project details at https://RandomNerdTutorials.com/micropython-programming-with-esp32-and-esp8266/
from machine import Pin, ADC
from time import sleep
pot = ADC(Pin(39))
pot.atten(ADC.ATTN_11DB) #Full range: 3.3v
while True:
pot_value = pot.read()
print(pot_value)
sleep(0.1)
Note that the readings range from 0 to 4095 which is 2^12 or 12-bit.
Photoresistance¶
Used the this site to test Light dependant resistor (LDR).
from machine import ADC, Pin
import time
class LDR:
"""This class read a value from a light dependent resistor (LDR)"""
def __init__(self, pin, min_value=0, max_value=100):
"""
Initializes a new instance.
:parameter pin A pin that's connected to an LDR.
:parameter min_value A min value that can be returned by value() method.
:parameter max_value A max value that can be returned by value() method.
"""
if min_value >= max_value:
raise Exception('Min value is greater or equal to max value')
# initialize ADC (analog to digital conversion)
self.adc = ADC(Pin(39))
# set 11dB input attenuation (voltage range roughly 0.0v - 3.6v)
self.adc.atten(ADC.ATTN_11DB)
self.min_value = min_value
self.max_value = max_value
def read(self):
"""
Read a raw value from the LDR.
:return A value from 0 to 4095.
"""
return self.adc.read()
def value(self):
"""
Read a value from the LDR in the specified range.
:return A value from the specified [min, max] range.
"""
return (self.max_value - self.min_value) * self.read() / 4095
# initialize an LDR
ldr = LDR(34)
while True:
# read a value from the LDR
value = ldr.value()
print('value = {}'.format(value))
# a little delay
time.sleep(0.5)
Investigating Electronics For Our Project¶
After the ESP appetizer, we focused on Arduino electronics that we believed could be used in our project.
Using BLE For Detecting Devices In Proximity¶
We used the Adafruit Feather Sense which has a lot of built in features including bluetooth, in order to come up with a method of detecting if a device is in proximity of another using BLE.
Bluetooth Low Energy (BLE) is a wireless personal area network technology that uses considerably less power and costs less than normal Bluetooth, yet offers similar range. It is typically used to transfer small amounts of data between nearby devices, including the use of proximity sensors.
To use BLE there must be a central device which scans the vicinity for advertisements, and a peripheral devices which emits these advertisements.
To do this, we used example codes that are available in Arduino IDE when an Adafruit Feather is connected and properly installed to it (Refer to Week 1).
Click to expand for Central Device Code
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
Author: KTOWN (Kevin Townsend)
Copyright (C) Adafruit Industries 2017
*********************************************************************/
/* This example scans for advertising devices (peripherals) in range,
* looking for a specific UUID in the advertising packet. When this
* UUID is found, it will display an alert, sorting devices detected
* in range by their RSSI value, which is an approximate indicator of
* proximity (though highly dependent on environmental obstacles, etc.).
*
* A simple 'bubble sort' algorithm is used, along with a simple
* set of decisions about whether and where to insert new records
* in the record list.
*
* This example is intended to be used with multiple peripherals
* devices running the *_peripheral.ino version of this application.
*
* VERBOSE_OUTPUT
* --------------
* Verbose advertising packet analysis can be enabled for
* advertising packet contents to help debug issues with the
* advertising payloads, with fully parsed data displayed in the
* Serial Monitor.
*
* ARRAY_SIZE
* ----------
* The numbers of peripherals tracked and sorted can be set via the
* ARRAY_SIZE macro. Must be at least 2.
*
* TIMEOUT_MS
* ----------
* This value determines the number of milliseconds before a tracked
* peripheral has it's last sample 'invalidated', such as when a device
* is tracked and then goes out of range.
*
* ENABLE_TFT
* ----------
* An ILI9341 based TFT can optionally be used to display proximity
* alerts. The Adafruit TFT Feather Wing is recommended and is the only
* device tested with this functionality.
*
* ENABLE_OLED
* -----------
* An SSD1306 128x32 based OLED can optionally be used to display
* proximity alerts. The Adafruit OLED Feather Wing is recommended and
* is the only device tested with this functionality.
*/
#include <string.h>
#include <bluefruit.h>
#include <SPI.h>
#define VERBOSE_OUTPUT (0) // Set this to 1 for verbose adv packet output to the serial monitor
#define ARRAY_SIZE (4) // The number of RSSI values to store and compare
#define TIMEOUT_MS (2500) // Number of milliseconds before a record is invalidated in the list
#define ENABLE_TFT (0) // Set this to 1 to enable ILI9341 TFT display support
#define ENABLE_OLED (0) // Set this to 1 to enable SSD1306 128x32 OLED display support
#if (ARRAY_SIZE <= 1)
#error "ARRAY_SIZE must be at least 2"
#endif
#if (ENABLE_TFT) && (ENABLE_OLED)
#error "ENABLE_TFT and ENABLE_OLED can not both be set at the same time"
#endif
// Custom UUID used to differentiate this device.
// Use any online UUID generator to generate a valid UUID.
// Note that the byte order is reversed ... CUSTOM_UUID
// below corresponds to the follow value:
// df67ff1a-718f-11e7-8cf7-a6006ad3dba0
const uint8_t CUSTOM_UUID[] =
{
0xA0, 0xDB, 0xD3, 0x6A, 0x00, 0xA6, 0xF7, 0x8C,
0xE7, 0x11, 0x8F, 0x71, 0x1A, 0xFF, 0x67, 0xDF
};
BLEUuid uuid = BLEUuid(CUSTOM_UUID);
/* TFT setup if the TFT display is available */
#if (ENABLE_TFT)
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
/* Pin setup for the TFT display over SPI */
#ifdef ARDUINO_NRF52832_FEATHER
#define TFT_DC 11
#define TFT_CS 31
#define STMPE_CS 30
#define SD_CS 27
#else
#error "Unknown target. Please make sure you are using an nRF52 Feather and BSP 0.6.5 or higher!"
#endif
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
#endif
/* 128x32 OLED setup if the OLED display is available */
#if (ENABLE_OLED)
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
/* Pin setup for the OLED display */
#ifdef ARDUINO_NRF52832_FEATHER
#define BUTTON_A 31
#define BUTTON_B 30
#define BUTTON_C 27
#else
#error "Unknown target. Please make sure you are using an nRF52 Feather and BSP 0.6.5 or higher!"
#endif
Adafruit_SSD1306 oled = Adafruit_SSD1306();
#endif
/* This struct is used to track detected nodes */
typedef struct node_record_s
{
uint8_t addr[6]; // Six byte device address
int8_t rssi; // RSSI value
uint32_t timestamp; // Timestamp for invalidation purposes
int8_t reserved; // Padding for word alignment
} node_record_t;
node_record_t records[ARRAY_SIZE];
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println("Bluefruit52 Central Proximity Example");
Serial.println("-------------------------------------\n");
/* Enable the TFT display if available */
#if ENABLE_TFT
tft.begin();
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(40, 0);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.println("DUNKIN");
#endif
/* Enable the OLED display if available */
#if ENABLE_OLED
oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
oled.display();
#endif
/* Clear the results list */
memset(records, 0, sizeof(records));
for (uint8_t i = 0; i<ARRAY_SIZE; i++)
{
// Set all RSSI values to lowest value for comparison purposes,
// since 0 would be higher than any valid RSSI value
records[i].rssi = -128;
}
/* Enable both peripheral and central modes */
if ( !Bluefruit.begin(1, 1) )
{
Serial.println("Unable to init Bluefruit");
while(1)
{
digitalToggle(LED_RED);
delay(100);
}
}
else
{
Serial.println("Bluefruit initialized (central mode)");
}
Bluefruit.setTxPower(4); // Check bluefruit.h for supported values
/* Set the LED interval for blinky pattern on BLUE LED */
Bluefruit.setConnLedInterval(250);
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Filter out packet with a min rssi
* - Interval = 100 ms, window = 50 ms
* - Use active scan (used to retrieve the optional scan response adv packet)
* - Start(0) = will scan forever since no timeout is given
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.filterRssi(-80); // Only invoke callback for devices with RSSI >= -80 dBm
Bluefruit.Scanner.filterUuid(uuid); // Only invoke callback if the target UUID was found
//Bluefruit.Scanner.filterMSD(0xFFFF); // Only invoke callback when MSD is present with the specified Company ID
Bluefruit.Scanner.setInterval(160, 80); // in units of 0.625 ms
Bluefruit.Scanner.useActiveScan(true); // Request scan response data
Bluefruit.Scanner.start(0); // 0 = Don't stop scanning after n seconds
Serial.println("Scanning ...");
}
/* This callback handler is fired every time a valid advertising packet is detected */
void scan_callback(ble_gap_evt_adv_report_t* report)
{
node_record_t record;
/* Prepare the record to try to insert it into the existing record list */
memcpy(record.addr, report->peer_addr.addr, 6); /* Copy the 6-byte device ADDR */
record.rssi = report->rssi; /* Copy the RSSI value */
record.timestamp = millis(); /* Set the timestamp (approximate) */
/* Attempt to insert the record into the list */
if (insertRecord(&record) == 1) /* Returns 1 if the list was updated */
{
printRecordList(); /* The list was updated, print the new values */
Serial.println("");
/* Display the device list on the TFT if available */
#if ENABLE_TFT
renderResultsToTFT();
#endif
/* Display the device list on the OLED if available */
#if ENABLE_OLED
renderResultsToOLED();
#endif
}
/* Fully parse and display the advertising packet to the Serial Monitor
* if verbose/debug output is requested */
#if VERBOSE_OUTPUT
uint8_t len = 0;
uint8_t buffer[32];
memset(buffer, 0, sizeof(buffer));
/* Display the timestamp and device address */
if (report->type.scan_response)
{
Serial.printf("[SR%10d] Packet received from ", millis());
}
else
{
Serial.printf("[ADV%9d] Packet received from ", millis());
}
// MAC is in little endian --> print reverse
Serial.printBufferReverse(report->peer_addr.addr, 6, ':');
Serial.println("");
/* Raw buffer contents */
Serial.printf("%14s %d bytes\n", "PAYLOAD", report->data.len);
if (report->data.len)
{
Serial.printf("%15s", " ");
Serial.printBuffer(report->data.p_data, report->data.len, '-');
Serial.println();
}
/* RSSI value */
Serial.printf("%14s %d dBm\n", "RSSI", report->rssi);
/* Adv Type */
Serial.printf("%14s ", "ADV TYPE");
if ( report->type.connectable )
{
Serial.print("Connectable ");
}else
{
Serial.print("Non-connectable ");
}
if ( report->type.directed )
{
Serial.println("directed");
}else
{
Serial.println("undirected");
}
/* Shortened Local Name */
if(Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME, buffer, sizeof(buffer)))
{
Serial.printf("%14s %s\n", "SHORT NAME", buffer);
memset(buffer, 0, sizeof(buffer));
}
/* Complete Local Name */
if(Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME, buffer, sizeof(buffer)))
{
Serial.printf("%14s %s\n", "COMPLETE NAME", buffer);
memset(buffer, 0, sizeof(buffer));
}
/* TX Power Level */
if (Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_TX_POWER_LEVEL, buffer, sizeof(buffer)))
{
Serial.printf("%14s %i\n", "TX PWR LEVEL", buffer[0]);
memset(buffer, 0, sizeof(buffer));
}
/* Check for UUID16 Complete List */
len = Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE, buffer, sizeof(buffer));
if ( len )
{
printUuid16List(buffer, len);
}
/* Check for UUID16 More Available List */
len = Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE, buffer, sizeof(buffer));
if ( len )
{
printUuid16List(buffer, len);
}
/* Check for UUID128 Complete List */
len = Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE, buffer, sizeof(buffer));
if ( len )
{
printUuid128List(buffer, len);
}
/* Check for UUID128 More Available List */
len = Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE, buffer, sizeof(buffer));
if ( len )
{
printUuid128List(buffer, len);
}
/* Check for BLE UART UUID */
if ( Bluefruit.Scanner.checkReportForUuid(report, BLEUART_UUID_SERVICE) )
{
Serial.printf("%14s %s\n", "BLE UART", "UUID Found!");
}
/* Check for DIS UUID */
if ( Bluefruit.Scanner.checkReportForUuid(report, UUID16_SVC_DEVICE_INFORMATION) )
{
Serial.printf("%14s %s\n", "DIS", "UUID Found!");
}
/* Check for Manufacturer Specific Data */
len = Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, buffer, sizeof(buffer));
if (len)
{
Serial.printf("%14s ", "MAN SPEC DATA");
Serial.printBuffer(buffer, len, '-');
Serial.println();
memset(buffer, 0, sizeof(buffer));
}
Serial.println();
#endif
// For Softdevice v6: after received a report, scanner will be paused
// We need to call Scanner resume() to continue scanning
Bluefruit.Scanner.resume();
}
#if ENABLE_TFT
void renderResultsToTFT(void)
{
tft.fillRect(0, 0, 240, ARRAY_SIZE*8, ILI9341_BLACK);
tft.setTextSize(1);
tft.setCursor(0, 0);
for (uint8_t i=0; i<ARRAY_SIZE; i++)
{
if (records[i].addr[0] <= 0xF) tft.print("0");
tft.print(records[i].addr[0], HEX);
tft.print(":");
if (records[i].addr[1] <= 0xF) tft.print("0");
tft.print(records[i].addr[1], HEX);
tft.print(":");
if (records[i].addr[2] <= 0xF) tft.print("0");
tft.print(records[i].addr[2], HEX);
tft.print(":");
if (records[i].addr[3] <= 0xF) tft.print("0");
tft.print(records[i].addr[3], HEX);
tft.print(":");
if (records[i].addr[4] <= 0xF) tft.print("0");
tft.print(records[i].addr[4], HEX);
tft.print(":");
if (records[i].addr[5] <= 0xF) tft.print("0");
tft.print(records[i].addr[5], HEX);
tft.print(" ");
tft.print(records[i].rssi);
tft.print(" [");
tft.print(records[i].timestamp);
tft.println("] ");
}
}
#endif
#if ENABLE_OLED
void renderResultsToOLED(void)
{
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(WHITE);
for (uint8_t i=0; i<ARRAY_SIZE; i++)
{
if (i>=4)
{
/* We can only display four records on a 128x32 pixel display */
oled.display();
return;
}
if (records[i].rssi != -128)
{
oled.setCursor(0, i*8);
if (records[i].addr[0] <= 0xF) oled.print("0");
oled.print(records[i].addr[0], HEX);
oled.print(":");
if (records[i].addr[1] <= 0xF) oled.print("0");
oled.print(records[i].addr[1], HEX);
oled.print(":");
if (records[i].addr[2] <= 0xF) oled.print("0");
oled.print(records[i].addr[2], HEX);
oled.print(":");
if (records[i].addr[3] <= 0xF) oled.print("0");
oled.print(records[i].addr[3], HEX);
oled.print(":");
if (records[i].addr[4] <= 0xF) oled.print("0");
oled.print(records[i].addr[4], HEX);
oled.print(":");
if (records[i].addr[5] <= 0xF) oled.print("0");
oled.print(records[i].addr[5], HEX);
oled.print(" ");
oled.println(records[i].rssi);
}
}
oled.display();
}
#endif
/* Prints a UUID16 list to the Serial Monitor */
void printUuid16List(uint8_t* buffer, uint8_t len)
{
Serial.printf("%14s %s", "16-Bit UUID");
for(int i=0; i<len; i+=2)
{
uint16_t uuid16;
memcpy(&uuid16, buffer+i, 2);
Serial.printf("%04X ", uuid16);
}
Serial.println();
}
/* Prints a UUID128 list to the Serial Monitor */
void printUuid128List(uint8_t* buffer, uint8_t len)
{
(void) len;
Serial.printf("%14s %s", "128-Bit UUID");
// Print reversed order
for(int i=0; i<16; i++)
{
const char* fm = (i==4 || i==6 || i==8 || i==10) ? "-%02X" : "%02X";
Serial.printf(fm, buffer[15-i]);
}
Serial.println();
}
/* Prints the current record list to the Serial Monitor */
void printRecordList(void)
{
for (uint8_t i = 0; i<ARRAY_SIZE; i++)
{
Serial.printf("[%i] ", i);
Serial.printBuffer(records[i].addr, 6, ':');
Serial.printf(" %i (%u ms)\n", records[i].rssi, records[i].timestamp);
}
}
/* This function performs a simple bubble sort on the records array */
/* It's slow, but relatively easy to understand */
/* Sorts based on RSSI values, where the strongest signal appears highest in the list */
void bubbleSort(void)
{
int inner, outer;
node_record_t temp;
for(outer=0; outer<ARRAY_SIZE-1; outer++)
{
for(inner=outer+1; inner<ARRAY_SIZE; inner++)
{
if(records[outer].rssi < records[inner].rssi)
{
memcpy((void *)&temp, (void *)&records[outer], sizeof(node_record_t)); // temp=records[outer];
memcpy((void *)&records[outer], (void *)&records[inner], sizeof(node_record_t)); // records[outer] = records[inner];
memcpy((void *)&records[inner], (void *)&temp, sizeof(node_record_t)); // records[inner] = temp;
}
}
}
}
/* This function will check if any records in the list
* have expired and need to be invalidated, such as when
* a device goes out of range.
*
* Returns the number of invalidated records, or 0 if
* nothing was changed.
*/
int invalidateRecords(void)
{
uint8_t i;
int match = 0;
/* Not enough time has elapsed to avoid an underflow error */
if (millis() <= TIMEOUT_MS)
{
return 0;
}
/* Check if any records have expired */
for (i=0; i<ARRAY_SIZE; i++)
{
if (records[i].timestamp) // Ignore zero"ed records
{
if (millis() - records[i].timestamp >= TIMEOUT_MS)
{
/* Record has expired, zero it out */
memset(&records[i], 0, sizeof(node_record_t));
records[i].rssi = -128;
match++;
}
}
}
/* Resort the list if something was zero'ed out */
if (match)
{
// Serial.printf("Invalidated %i records!\n", match);
bubbleSort();
}
return match;
}
/* This function attempts to insert the record if it is larger than the smallest valid RSSI entry */
/* Returns 1 if a change was made, otherwise 0 */
int insertRecord(node_record_t *record)
{
uint8_t i;
/* Invalidate results older than n milliseconds */
invalidateRecords();
/* Record Insertion Workflow:
*
* START
* |
* \ /
* +-------------+
* 1. | BUBBLE SORT | // Put list in known state!
* +-------------+
* |
* _____\ /_____
* / ENTRY \ YES
* 2. < EXISTS W/THIS > ------------------+
* \ ADDRESS? / |
* ----------- |
* | NO |
* | |
* ______\ /______ |
* / IS \ YES |
* 3. < THERE A ZERO'ED >------------------+
* \ RECORD? / |
* ------------- |
* | NO |
* | |
* ______\ /________ |
* / IS THE \ YES |
* 4.< RECORD'S RSSI >= >----------------|
* \ THE LOWEST RSSI? / |
* ---------------- |
* | NO |
* | |
* \ / \ /
* +---------------+ +----------------+
* | IGNORE RECORD | | REPLACE RECORD |
* +---------------+ +----------------+
* | |
* | \ /
* \ / +----------------+
* EXIT <------------- | BUBBLE SORT |
* +----------------+
*/
/* 1. Bubble Sort
* This puts the lists in a known state where we can make
* certain assumptions about the last record in the array. */
bubbleSort();
/* 2. Check for a match on existing device address */
/* Replace it if a match is found, then sort */
uint8_t match = 0;
for (i=0; i<ARRAY_SIZE; i++)
{
if (memcmp(records[i].addr, record->addr, 6) == 0)
{
match = 1;
}
if (match)
{
memcpy(&records[i], record, sizeof(node_record_t));
goto sort_then_exit;
}
}
/* 3. Check for zero'ed records */
/* Insert if a zero record is found, then sort */
for (i=0; i<ARRAY_SIZE; i++)
{
if (records[i].rssi == -128)
{
memcpy(&records[i], record, sizeof(node_record_t));
goto sort_then_exit;
}
}
/* 4. Check RSSI of the lowest record */
/* Replace if >=, then sort */
if (records[ARRAY_SIZE-1].rssi <= record->rssi)
{
memcpy(&records[ARRAY_SIZE-1], record, sizeof(node_record_t));
goto sort_then_exit;
}
/* Nothing to do ... RSSI is lower than the last value, exit and ignore */
return 0;
sort_then_exit:
/* Bubble sort */
bubbleSort();
return 1;
}
void loop()
{
/* Toggle red LED every second */
digitalToggle(LED_RED);
/* Invalidate old results once per second in addition
* to the invalidation in the callback handler. */
/* ToDo: Update to use a mutex or semaphore since this
* can lead to list corruption as-is if the scann results
* callback is fired in the middle of the invalidation
* function. */
if (invalidateRecords())
{
/* The list was updated, print the new values */
printRecordList();
Serial.println("");
/* Display the device list on the TFT if available */
#if ENABLE_TFT
renderResultsToTFT();
#endif
/* Display the device list on the OLED if available */
#if ENABLE_OLED
renderResultsToOLED();
#endif
}
delay(1000);
}
Click to expand for Peripheral Device Code
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
Author: KTOWN (Kevin Townsend)
Copyright (C) Adafruit Industries 2017
*********************************************************************/
/* This example constantly advertises a custom 128-bit UUID, and is
* intended to be used in combination with a Central sketch that scans
* for this UUID, and then displays an alert message, sorting matching
* devices by their RSSI level which is an approximate indication of
* distance (although highly subject to environmental obstacles).
*
* By including a custom UUID in the advertising packet, we can easily
* filter the scan results on the Central device, rather than manually
* parsing the advertising packet(s) of every device in range.
*
* This example is intended to be run with the *_central.ino version
* of this application.
*/
#include <bluefruit.h>
#include <ble_gap.h>
// Software Timer for blinking RED LED
SoftwareTimer blinkTimer;
// Custom UUID used to differentiate this device.
// Use any online UUID generator to generate a valid UUID.
// Note that the byte order is reversed ... CUSTOM_UUID
// below corresponds to the follow value:
// df67ff1a-718f-11e7-8cf7-a6006ad3dba0
const uint8_t CUSTOM_UUID[] =
{
0xA0, 0xDB, 0xD3, 0x6A, 0x00, 0xA6, 0xF7, 0x8C,
0xE7, 0x11, 0x8F, 0x71, 0x1A, 0xFF, 0x67, 0xDF
};
BLEUuid uuid = BLEUuid(CUSTOM_UUID);
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println("Bluefruit52 Peripheral Proximity Example");
Serial.println("----------------------------------------\n");
// Initialize blinkTimer for 1000 ms and start it
blinkTimer.begin(1000, blink_timer_callback);
blinkTimer.start();
if (!Bluefruit.begin())
{
Serial.println("Unable to init Bluefruit");
while(1)
{
digitalToggle(LED_RED);
delay(100);
}
}
else
{
Serial.println("Bluefruit initialized (peripheral mode)");
}
Bluefruit.setTxPower(4); // Check bluefruit.h for supported values
// Set up and start advertising
startAdv();
Serial.println("Advertising started");
}
void startAdv(void)
{
// Note: The entire advertising packet is limited to 31 bytes!
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Preferred Solution: Add a custom UUID to the advertising payload, which
// we will look for on the Central side via Bluefruit.Scanner.filterUuid(uuid);
// A valid 128-bit UUID can be generated online with almost no chance of conflict
// with another device or etup
Bluefruit.Advertising.addUuid(uuid);
/*
// Alternative Solution: Manufacturer Specific Data (MSD)
// You could also send a custom MSD payload and filter for the 'Company ID'
// via 'Bluefruit.Scanner.filterMSD(CID);', although this does require a
// valid CID, which is why the UUID method above is more appropriate in
// most situations. For a complete list of valid company IDs see:
// https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
// For test purposes, 0xFFFF CAN be used, but according to the Bluetooth SIG:
// > "This value may be used in the internal and interoperability tests before a
// > Company ID has been assigned. This value shall not be used in shipping end
// > products."
uint8_t msd_payload[4]; // Two bytes are required for the CID, so we have 2 bytes user data, expand as needed
uint16_t msd_cid = 0xFFFF;
memset(msd_payload, 0, sizeof(msd_payload));
memcpy(msd_payload, (uint8_t*)&msd_cid, sizeof(msd_cid));
msd_payload[2] = 0x11;
msd_payload[3] = 0x22;
Bluefruit.Advertising.addData(BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, msd_payload, sizeof(msd_payload));
*/
// Not enough room in the advertising packet for name
// so store it in the Scan Response instead
Bluefruit.ScanResponse.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in units of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start();
}
void loop()
{
}
/**
* Software Timer callback is invoked via a built-in FreeRTOS thread with
* minimal stack size. Therefore it should be as simple as possible. If
* a periodically heavy task is needed, please use Scheduler.startLoop() to
* create a dedicated task for it.
*
* More information http://www.freertos.org/RTOS-software-timer.html
*/
void blink_timer_callback(TimerHandle_t xTimerID)
{
(void) xTimerID;
digitalToggle(LED_RED);
}
I honestly don’t understand most of the code, but I do understand how to use it.
For the devices to identify each other they must be set to the same UUID, for testing I did not change this from the default value in the example, however, for actual use you can use an online UUID generator and then change the default value in both the central and peripheral code to this new value.
Universally Unique Identifier (UUID) is a 128-bit value used to uniquely identify an object or entity on the internet. The chances that a UUID is not unique are not zero but are negligible.
After setting the UUID, upload the code to the peripheral device (for the Adafruit Feather you must press the reset button and wait for the LED to turn green before uploading code). The device should then advertise the UUID.
You can then upload the code to the cental device, which will scan for UUID in its proximity. If it detects the peripheral device, it should connect to it.
After the central device finds a peripheral device it displays in the serial monitor the address of the device (idk what type of address), its RSSI value, and the length of time the peripheral device has been on in milliseconds.
Received Signal Strength Indicator (RSSI) is a measure that represents the relative quality level of a Bluetooth signal received on a device. As the RSSI value decreases (becomes more negative), the proximity (distance) between the devices tends to increase. RSSI is measured in negative dBm and the closer it is to 0 the better the signal is.
Using LCD Screen with Accelerometer¶
I needed to test out using multiple I2C devices at the same time, and to try out using an accelerometer for detecting a door opening.
To do this I first used example code of the accelerometer to print out the values it reads, and after placing it on the “door” I wanted to try I noticed which values were moving when opening and closing the door. I found that in this case when the door is opened the a.acceleration.y value is < -2, and when the door is closed the a.acceleration.y value is around 0.
Based on these observations I modified the example code to print out Door opened or Door closed on the LCD:
Click to expand for Code
#include <LiquidCrystal_I2C.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
Adafruit_MPU6050 mpu;
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup() {
lcd.init();
lcd.backlight();
Serial.begin(115200);
while (!Serial)
delay(10); // will pause Zero, Leonardo, etc until serial console opens
Serial.println("Adafruit MPU6050 test!");
// Try to initialize!
if (!mpu.begin()) {
Serial.println("Failed to find MPU6050 chip");
while (1) {
delay(10);
}
}
Serial.println("MPU6050 Found!");
mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
Serial.print("Accelerometer range set to: ");
switch (mpu.getAccelerometerRange()) {
case MPU6050_RANGE_2_G:
Serial.println("+-2G");
break;
case MPU6050_RANGE_4_G:
Serial.println("+-4G");
break;
case MPU6050_RANGE_8_G:
Serial.println("+-8G");
break;
case MPU6050_RANGE_16_G:
Serial.println("+-16G");
break;
}
mpu.setGyroRange(MPU6050_RANGE_500_DEG);
Serial.print("Gyro range set to: ");
switch (mpu.getGyroRange()) {
case MPU6050_RANGE_250_DEG:
Serial.println("+- 250 deg/s");
break;
case MPU6050_RANGE_500_DEG:
Serial.println("+- 500 deg/s");
break;
case MPU6050_RANGE_1000_DEG:
Serial.println("+- 1000 deg/s");
break;
case MPU6050_RANGE_2000_DEG:
Serial.println("+- 2000 deg/s");
break;
}
mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
Serial.print("Filter bandwidth set to: ");
switch (mpu.getFilterBandwidth()) {
case MPU6050_BAND_260_HZ:
Serial.println("260 Hz");
break;
case MPU6050_BAND_184_HZ:
Serial.println("184 Hz");
break;
case MPU6050_BAND_94_HZ:
Serial.println("94 Hz");
break;
case MPU6050_BAND_44_HZ:
Serial.println("44 Hz");
break;
case MPU6050_BAND_21_HZ:
Serial.println("21 Hz");
break;
case MPU6050_BAND_10_HZ:
Serial.println("10 Hz");
break;
case MPU6050_BAND_5_HZ:
Serial.println("5 Hz");
break;
}
Serial.println("");
delay(100);
}
void loop() {
/* Get new sensor events with the readings */
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
/* Print out the values */
Serial.print("Acceleration X: ");
Serial.print(a.acceleration.x);
Serial.print(", Y: ");
Serial.print(a.acceleration.y);
Serial.print(", Z: ");
Serial.print(a.acceleration.z);
Serial.println(" m/s^2");
Serial.print("Rotation X: ");
Serial.print(g.gyro.x);
Serial.print(", Y: ");
Serial.print(g.gyro.y);
Serial.print(", Z: ");
Serial.print(g.gyro.z);
Serial.println(" rad/s");
Serial.print("Temperature: ");
Serial.print(temp.temperature);
Serial.println(" degC");
Serial.println("");
delay(500);
if (a.acceleration.y < -2) {
lcd.setCursor(0, 0);
lcd.print("Box Open");
} else {
lcd.setCursor(0, 0);
lcd.print("Box Closed");
}
}