I have a Raspberry PI with a Cambridge Scientific USB adapter on which I've un the following:
sudo hciconfig hci0 up
sudo hciconfig hci0 leadv 3
sudo hcitool -i hci0 cmd 0x08 0x0008 1E 02 01 06 1A FF 4C 00 02 15 C7 C1 A1 BF BB 00 4C AD 87 04 9F 2D 29 17 DE D2 00 00 00 00 C8 00
I have written a MacOS Cocoa App as follows:
import Cocoa
import CoreBluetooth
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
BeaconScanner(type: BeaconType.iBeacon).scanEverySecond()
}
}
enum BeaconType {
case iBeacon
}
struct Beacon {
var uuid: NSUUID!
var major: UInt16!
var minor: UInt16!
var power: Int8!
var peripheral: CBPeripheral!
var advertisementData: NSDictionary!
var RSSI: NSNumber!
init(peripheral: CBPeripheral, advertisementData: NSDictionary, RSSI: NSNumber) {
self.peripheral = peripheral
self.advertisementData = advertisementData
self.RSSI = RSSI
}
private func getBytes(data: Any?, from: Int, count: Int) -> [CUnsignedChar] {
var bytes = [CUnsignedChar](repeating: 0, count: count)
(data! as AnyObject).getBytes(&bytes, range: NSMakeRange(from, count))
return bytes
}
private func getBytesAsUshort(data: Any?, from: Int) -> ushort {
var bytes = ushort()
(data! as AnyObject).getBytes(&bytes, range: NSMakeRange(from, 2))
return NSSwapShort(bytes)
}
private func getBytesAsInt8(data: Any?, from: Int) -> Int8 {
var bytes = Int8()
(data! as AnyObject).getBytes(&bytes, range: NSMakeRange(from, 1))
return bytes
}
func checkAdvertisementDataForPossibleiBeaconData() {
let beaconStandard: [CUnsignedChar] = [0x4c, 0x00, 0x02, 0x15]
var data = advertisementData.value(forKey: "kCBAdvDataManufacturerData")
if(data != nil) {
data = data as! Data
var beaconStandardBytes = [CUnsignedChar](repeating: 0, count: 4)
(data! as AnyObject).getBytes(&beaconStandardBytes, range: NSMakeRange(0,4))
if((data! as AnyObject).length >= 25 && beaconStandardBytes.elementsEqual(beaconStandard)) {
let uuid = NSUUID.init(uuidBytes: getBytes(data: data, from: 4, count: 16)) as UUID
let major = getBytesAsUshort(data: data, from: 20)
let minor = getBytesAsUshort(data: data, from: 22)
let power = getBytesAsInt8(data: data, from: 24)
let distance = calculateBeaconDistance(Int(power), RSSI: Int(truncating: RSSI))
print("BeaconStandard=\(beaconStandard), UUID=\(uuid.uuidString), major=\(major), minor=\(minor), power=\(power), RSSI=\(String(describing: RSSI)), Distance=\(String(format: "%.2f", distance))m")
}
}
}
func calculateBeaconDistance(_ power: Int, RSSI: Int) -> Double {
let ratio = Double(RSSI) * 1.0 / Double(power)
return ratio < 1.0 ? pow(ratio, 10.0) : round(10*((0.89976) * pow(ratio, 7.7095) + 0.111)) / 10
}
}
class BeaconScanner: NSObject, CBCentralManagerDelegate {
var cbManager: CBCentralManager!
var timer: Timer!
var type: BeaconType!
init(type: BeaconType) {
self.type = type
}
func scanEverySecond() {
cbManager = CBCentralManager(delegate: self, queue: nil)
self.timer = Timer.scheduledTimer(timeInterval: TimeInterval(1), target: self, selector: #selector(scan), userInfo: nil, repeats: true)
}
#objc func scan() {
if(cbManager != nil && cbManager.state.rawValue==CBManagerState.poweredOn.rawValue) {
cbManager.scanForPeripherals(withServices: nil, options: nil)
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
Beacon(peripheral: peripheral, advertisementData: advertisementData as NSDictionary, RSSI: RSSI).checkAdvertisementDataForPossibleiBeaconData()
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if(!(central.state.rawValue == CBManagerState.poweredOn.rawValue)) {
print("In order to user this tool, Bluetooth must be switched on\n")
exit(EXIT_FAILURE)
}
}
}
This seems to work just fine. I get the following:
BeaconStandard=[76, 0, 2, 21], UUID=C7C1A1BF-BB00-4CAD-8704-9F2D2917DED2, major=0, minor=0, power=-56, RSSI=Optional(-74), Distance=7.80m
BeaconStandard=[76, 0, 2, 21], UUID=C7C1A1BF-BB00-4CAD-8704-9F2D2917DED2, major=0, minor=0, power=-56, RSSI=Optional(-76), Distance=9.60m
I then built an iOS app simply copying the ViewController code replacing
import Cocoa
import CoreBluetooth
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
BeaconScanner(type: BeaconType.iBeacon).scanEverySecond()
}
}
with
import UIKit
import CoreBluetooth
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
BeaconScanner(type: BeaconType.iBeacon).scanEverySecond()
}
}
I ran the code for about 5 minutes on iOS but got no hits.
I added some debug code and I do see hits from other Bluetooth devices, but none from the PI.
MacOS is running 10.14.6. iOS is running 12.3.1 and XCode is running 10.3.
Is this a hardware limitation? I know the CSR Bluetooth is pretty cheap. Or do I have a problem in the code?
Related
I am new to SWift and have tried to play beep sound with fixed frequency, say 600Hz. That's it. Any suggestion is welcome.
Here is an example using AVFoundation I tried, but it plays no sound. I suspect many places but have no clues.
(1) initialzing player
(2) prepared buffer wrongly
(3) misused optional type and doing wrapping wrongly
(4) the code is okay but it does not play on simulator
(5) and surprising many other possibilities
Can you kindly help me?
import SwiftUI
import AVFoundation
struct BeepPlayer: View {
var body: some View {
Button(action: playBeep) {
Text("Play 600Hz Beep")
}
}
func playBeep() {
let engine = AVAudioEngine()
let player = AVAudioPlayerNode()
var mixer = engine.mainMixerNode
engine.attach(player)
var buffer = createBeepBuffer()
player.scheduleBuffer(buffer, at: nil, options: .loops, completionHandler: nil)
engine.connect(player, to: mixer, format: mixer.outputFormat(forBus: 0))
do {
try engine.start()
player.play()
} catch {
print("Error: \(error)")
}
player.stop()
}
func createBeepBuffer() -> AVAudioPCMBuffer {
let sampleRate: Double = 44100
let frequency: Float = 600
let duration: Double = 1
let frames = AVAudioFrameCount(44100 * duration)
let buffer = AVAudioPCMBuffer(pcmFormat: AVAudioFormat(standardFormatWithSampleRate: sampleRate, channels: 2)!, frameCapacity: frames)
buffer?.frameLength = frames
let amplitude: Float = 0.5
let increment = 2 * Float.pi * frequency / Float(sampleRate)
var currentPhase: Float = 0
for i in 0..<Int(frames) {
buffer?.floatChannelData?[0][i] = sin(currentPhase) * amplitude
buffer?.floatChannelData?[1][i] = sin(currentPhase) * amplitude
currentPhase += increment
}
return buffer!
}
}
I am developing a Qr Code and Barcode Scanner App, using CameraX and Zxing but the following class only works for Qr Code. I want to scan barcodes as well from any orientation on an android device
Code Scanning class:
public class QRCodeImageAnalyzer implements ImageAnalysis.Analyzer {
private QRCodeFoundListener listener;
public QRCodeImageAnalyzer(QRCodeFoundListener listener) {
this.listener = listener;
}
#SuppressLint("UnsafeOptInUsageError")
#Override
public void analyze(#NonNull ImageProxy imageProxy) {
Image image = imageProxy.getImage();
if (image.getFormat() == YUV_420_888 || image.getFormat() == YUV_422_888 || image.getFormat() == YUV_444_888) {
ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
byte[] imageData = new byte[byteBuffer.capacity()];
byteBuffer.get(imageData);
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(
imageData,
image.getWidth(), image.getHeight(),
0, 0,
image.getWidth(), image.getHeight(),
false
);
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
try {
Result result = new QRCodeMultiReader().decode(binaryBitmap);
listener.onQRCodeFound(result.getText());
} catch (FormatException | ChecksumException | NotFoundException e) {
listener.qrCodeNotFound();
}
}
imageProxy.close();
}
}
I also tried this class CameraX with MLKit: I tried the sample from the official docs of MLKit provided in the first answer but it scan nothing neither QR Code nor Barcode. Please take a look if I put things the wrong way.
import android.annotation.SuppressLint
import android.util.Log
import androidx.camera.core.ExperimentalGetImage
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import com.google.mlkit.vision.barcode.Barcode
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.common.InputImage
#UseExperimental(markerClass = [ExperimentalGetImage::class])
class QrCodeAnalyzer(
private val onQrCodesDetected: (qrCodes: List<Barcode>?) -> Unit
) : ImageAnalysis.Analyzer {
private val TAG: String = "Kamran"
private fun rotationDegreesToFirebaseRotation(rotationDegrees: Int): Int {
return when (rotationDegrees) {
0 -> 0
90 -> 1
180 -> 2
270 -> 3
else -> throw IllegalArgumentException("Not supported")
}
}
#SuppressLint("UnsafeOptInUsageError")
override fun analyze(image: ImageProxy) {
val rotation = rotationDegreesToFirebaseRotation(image.imageInfo.rotationDegrees)
image.image?.let{
val optionss = BarcodeScannerOptions.Builder()
.setBarcodeFormats(
Barcode.FORMAT_QR_CODE,
Barcode.FORMAT_EAN_8,
Barcode.FORMAT_EAN_13)
.build()
val imageValue = InputImage.fromMediaImage(it, image.imageInfo.rotationDegrees)
//val options = BarcodeScannerOptions.Builder().setBarcodeFormats(Barcode.FORMAT_QR_CODE).build()
val scanner = BarcodeScanning.getClient(optionss)
scanner.process(imageValue)
.addOnCompleteListener { barcodes ->
barcodes.result?.forEach { barcode ->
val bounds = barcode.boundingBox
val corners = barcode.cornerPoints
val rawValue = barcode.rawValue
}
onQrCodesDetected(barcodes.result)
image.image?.close()
image.close()
Log.d(TAG, "Successfully got inside onCompleteListener")
}
.addOnFailureListener { failure ->
failure.printStackTrace()
image.image?.close()
image.close()
}
}
}
}
I am not sure If using ZXing android library is a requirement (afaik it is no longer developed). If it is not a hard requirement, I would suggest you to give it a try for MLKit Barcode Scanner. It scans QR as well.
Also, you would probably find it easier to implement with CameraX as they already have multiple examples and sample code.
For example check this guide for step by step instructions.
Particularly this part.
You can configire the scanner first:
val options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(
Barcode.FORMAT_QR_CODE,
Barcode.FORMAT_EAN_8,
Barcode.FORMAT_EAN_13)
.build()
then;
private class YourImageAnalyzer : ImageAnalysis.Analyzer {
override fun analyze(imageProxy: ImageProxy) {
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
// Pass image to an ML Kit Vision API
// ...
}
}
}
finally this is how you get it decoded:
val result = scanner.process(image)
.addOnSuccessListener { barcodes ->
// Task completed successfully
// ...
}
.addOnFailureListener {
// Task failed with an exception
// ...
}
I am reading the UID of my RFID card and storing it in a variable called myUID.
After that I am authorizing to the card with the factory key and read block number 4 (which has been written to earlier) and store it in a string readBlock.
On the Arduino, I print out the variables onto the serial interface like so.
Serial.println(myUID);
Serial.println(readBlock);
On the client side, I use a Java program that reads in serial data. My program uses the Processing Library.
Serial mySerial;
PrintWriter output;
void setup() {
output = createWriter( "data.txt" );
mySerial = new Serial( this, Serial.list()[0], 9600 );
mySerial.bufferUntil('\n');
}
void draw(){
while (mySerial.available() > 0) {
String inBuffer = mySerial.readString();
if (inBuffer != null)
output.println(inBuffer);
}
}
void keyPressed() { // Press a key to save the data
output.flush(); // Write the remaining data
output.close(); // Finish the file
exit(); // Stop the program
}
Now my data.txt is expected to look like
xxx xxx xxx xxx (uid of card)
00 00 00 00 00 00 00 00 ... (read block from card)
but looks like
237 63 58 1
07
37 37 95
37
97 98 50 54 37 5
4 55 102 55 52
45 98
I have tried several things like readStringUntil('\n'); in the Processing Library but without success.
For everyone interested, I have fixed the problem myself with many hours of searching Google, so maybe this will help someone in the future:
I could fix it with this code:
import processing.serial.*;
int count = 0;
String input = "";
String fileName = dataPath("SET FILEPATH HERE");
Serial mySerial;
import java.io.*;
void setup() {
mySerial = new Serial(this, Serial.list()[0], 9600);
mySerial.bufferUntil('\n');
File f = new File(fileName);
if (f.exists()) {
f.delete();
}
}
void draw(){}
// listen to serial events happening
void serialEvent(Serial mySerial){
input = mySerial.readStringUntil('\n');
write(input, count);
count++;
}
// function for writing the data to the file
void write(String inputString, int counter) {
// should new data be appended or replace any old text in the file?
boolean append = false;
// just for my purpose, because I have got two lines of serial which need to get written to the file
//(Line 1: UID of card, Line 2: Read block of card)
if(counter < 2){
append = true;
}
else{
count = 0;
}
try {
File file = new File("D:/xampp/htdocs/pizza/src/rfid/data.txt");
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file, append);
BufferedWriter bw = new BufferedWriter(fw);
PrintWriter pw = new PrintWriter(bw);
pw.write(inputString + '\n');
pw.close();
}
catch(IOException ioe) {
System.out.println("Exception ");
ioe.printStackTrace();
}
}
hello I'am using Arduino and node js
I sent and recive data but the data incoming from arduino like this :
<Buffer 00 00 00 e0 e0 e0 00 e0 e0 e0>
<Buffer e0 e0 e0 e0 00 e0 e0 00 e0 00 e0 e0 e0>
How can i decode this to UTF8
arduino
int incomingByte = 0;
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}
void loop() {
if (Serial.available() > 0) {
incomingByte = Serial.read(); // read the incoming byte:
Serial.print(incomingByte);
}
}
You can use readable.setEncoding method from Node SerialPort class:
const SerialPort = require('serialport');
var port = new SerialPort("COM3").setEncoding('utf8');
In node.js you can use toString:
console.log(incomeBuffer.toString('utf8'))
incomingByte.toString()
encoding The character encoding to decode to. Default: 'utf8'
look at here
I used the Buffer class to do various conversions to get printable hex data.
const { SerialPort } = require('serialport')
const { Buffer } = require('buffer')
let buffer = []
port.on('data', function (data) {
buffer.push(Buffer.from(data, 'ascii'))
}})
// Button handler or something else
let buffer_ascii = ''
buffer.forEach(chunk => {
buffer_ascii += chunk.toString('hex')
})
console.log(buffer_ascii)
I am hoping to port some of my CoreBluetooth code from iOS to OS X. I've set up a shared set of CoreBluetooth wrappers which are consumed by both an iOS app and an OS X app in exactly the same manner with the same BLE devices.
Scanning for peripherals:
override init() {
super.init()
let queue = DispatchQueue.global(qos: .background)
centralManager = CBCentralManager(delegate: self, queue: queue)
}
func startScanning() {
let options: [String: Any] = [CBCentralManagerScanOptionAllowDuplicatesKey: true]
let deviceUUID = CBUUID(string: Project.Service.Device)
let recoveryUUID = CBUUID(string: Project.Service.DFURecovery)
centralManager?.scanForPeripherals(withServices: [deviceUUID, recoveryUUID], options: options)
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){
// Inspect advertisementData here to decipher what kind of device
}
On my iOS app, didDiscoverPeripheral is fired. Then when I inspect the advertisement data I get all the keys/values that I am expecting:
{
kCBAdvDataIsConnectable = 1;
kCBAdvDataLocalName = "My Device";
kCBAdvDataManufacturerData = <34045254 5877f283 43fdd12d ff530978 45000000 000050c2 6500>;
kCBAdvDataServiceData = {
Battery = <64>;
};
kCBAdvDataServiceUUIDs = (
"My Inforamtion"
);
}
However when this same code is run (scanning for the same devices) from an OS X app, the advertisement data is missing some of the fields.
{
kCBAdvDataIsConnectable = 1;
kCBAdvDataManufacturerData = <34045254 5877f36e 43fdd12d ff530978 45000000 000050c2 6500>;
}
The following key/value pairs are missing from advertisedData.
kCBAdvDataLocalName
kCBAdvDataServiceData
kCBAdvDataServiceUUIDs
I've tried adding those keys to the scanForPeripherals call like so:
let options: [String: Any] = [CBCentralManagerScanOptionAllowDuplicatesKey: true,
CBAdvertisementDataLocalNameKey: true,
CBAdvertisementDataServiceDataKey: true,
CBAdvertisementDataServiceUUIDsKey: true]
let deviceUUID = CBUUID(string: Nightlight.Service.Device)
let recoveryUUID = CBUUID(string: Nightlight.Service.DFURecovery)
centralManager?.scanForPeripherals(withServices: [deviceUUID, recoveryUUID], options: options)
With no effect.
OSX may call didDiscoverPeripheral multiple times per device, each call with different advertisementData. The solution I came up with is to write a cache.
import Foundation
import CoreBluetooth
class AdvertisementDataCache {
/// A dictionary of advertised data to peripheral.uuid
private var cache: [UUID: [String: Any]] = [:]
/// Appends advertisementData to our cache
/// for each unique `peripheral.uuid`.
func append(
advertisementData: [String: Any],
to peripheral: CBPeripheral
) -> [String: Any] {
// Join our cached adverts (our `cache` ivar)
// with new adverts (`advertisementData`)
let joined = advertisementData.reduce(
cache[peripheral.identifier] ?? [:]
) {
var all = $0
all[$1.key] = $1.value
return all
}
// write back to our private iVar
cache[peripheral.identifier] = joined
// Return all cached averts for this peripheral
return joined
}
/// Purges all cached avertisements for all peripherals
func clear() {
cache.removeAll()
}
}
And then in didDiscoverPeripheral:
private var advertisementDataCache = AdvertisementDataCache()
public func centralManager(
_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData adPart: [String: Any],
rssi RSSI: NSNumber
) {
let advertisementData = advertisementDataCache.append(
advertisementData: adPart,
to: peripheral
)
// advertisementData now contains all data contained in multiple callbacks
}
Update. I am using UIKitForMac Beta 2 (now called Catalyst) and am seeing the same issue. The solution listed above (caching/aggregating the adverstisment data) applies in this situation as well.