Computer >> คอมพิวเตอร์ >  >> ระบบ >> MAC

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

เคยต้องการที่จะเพิ่มการแสดงตนหรือการติดตามสถานที่ในโครงการหรือไม่? ผิดหวังกับวิธีแก้ปัญหา (หรือขาดมัน)?

ไม่ต้องกังวล คุณไม่ใช่คนเดียว!

ในโพสต์นี้ คุณจะได้เรียนรู้วิธีใช้แอปพลิเคชันการติดตามและการแจ้งเตือนขั้นพื้นฐาน เราจะใช้ Particle Argon และ Tile Mate

ในตอนท้ายคุณจะสามารถบอกได้ว่าไทล์นั้นมีอยู่หรือไม่ นอกจากนี้ เราจะใช้ Pushover เพื่อส่งการแจ้งเตือนแบบพุชไปยังอุปกรณ์ที่คุณเลือก

ไปกันเถอะ!

หมายเหตุ ก่อนที่เราจะเริ่มต้น โพสต์นี้ ยาว . คุณสามารถดาวน์โหลดเวอร์ชัน PDF เพื่อบันทึกและดูในภายหลังได้

การตรวจสอบเบื้องต้น

แนวคิดในการใช้ไทล์นั้นไม่ชัดเจนในแวบแรก ตามหลักการแล้ว การใช้โทรศัพท์ดูสมเหตุสมผลกว่า น่าเสียดายที่นี่ไม่ใช่ตัวเลือกที่ใช้การได้ จำเป็นต้องมีการวิจัยเพิ่มเติมและการสร้างแอป Bluetooth iOS

ความคิดในการใช้โทรศัพท์จึงหมดไป

แล้วฉันก็คิดว่า "อุปกรณ์อะไรทำ โฆษณาตลอดเวลา?"

นั่นคือสิ่งที่นำฉันไปสู่เส้นทางของตัวติดตามเช่นไทล์

หลังจากมาถึงก็มีการทดสอบตามธรรมเนียม ขั้นแรก แอปพลิเคชันไทล์

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

ฉันสามารถเชื่อมต่อและใช้อุปกรณ์ได้ ฉันยังทำให้มันเล่นท่วงทำนองที่ติดหู ?

จากนั้นฉันก็เปลี่ยนไปใช้แอพสแกนเนอร์ Bluetooth ตัวใดตัวหนึ่ง ฉันเลื่อนดูผลลัพธ์และบิงโกทั้งหมด มีกระเบื้อง!

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

ฉันยังรอสองสามชั่วโมงและตรวจสอบอีกครั้ง ฉันต้องการให้แน่ใจว่ามันไม่ได้ไปนอนหลังจากนั้นสักครู่ ปรากฎว่าโฆษณาอยู่เสมอ เท่าที่ฉันสามารถบอกได้ ทุกๆ 8 วินาที

การทดสอบทั้งหมดนี้นำไปสู่ข้อสรุปเดียว:สามารถใช้สำหรับการตรวจจับการมีอยู่ได้อย่างง่ายดาย

ขั้นตอนต่อไปในกระบวนการนี้คือการพยายามหาวิธีทำให้มันทำงานกับอาร์กอน

โฆษณา

ตามที่เราได้รวบรวมไว้ในขั้นตอนก่อนหน้านี้ เรารู้ว่าไทล์กำลังโฆษณาทุกๆ 8 วินาที ซึ่งหมายความว่าควรสแกนหาอุปกรณ์ใดๆ ก็ได้ เช่น Argon, Zenon หรือ Boron อย่างง่ายดาย

สำหรับตัวอย่างนี้ ฉันแนะนำให้คุณใช้ Argon เนื่องจากบลูทูธและเมชใช้วิทยุร่วมกัน เมื่อสแกนหากระเบื้อง Xenon ที่เชื่อมต่อกับ Mesh มักจะพลาดแพ็กเก็ตโฆษณา สิ่งนี้จะนำไปสู่การลบเท็จ (และความยุ่งยาก!)

ในทำนองเดียวกัน คุณจะต้องแน่ใจว่า Argon ของคุณไม่ได้เชื่อมต่อกับเครือข่ายแบบเมช คุณสามารถลบออกได้โดยใช้ CLI เชื่อมต่ออุปกรณ์กับคอมพิวเตอร์และเรียกใช้คำสั่งต่อไปนี้:

particle mesh remove <device name/ID>

ตรวจสอบให้แน่ใจว่าคุณได้เปลี่ยน <ชื่ออุปกรณ์/ID> ด้วยชื่อหรือ ID ของอุปกรณ์

เอาล่ะ กลับเข้าเรื่องกันดีกว่า

การโฆษณาสามารถมีวัตถุประสงค์ที่แตกต่างกันเล็กน้อยในบลูทูธ โดยปกติแล้วจะเป็นจุดเริ่มต้นของขั้นตอนการจับคู่ วิธีนี้จะทำให้อุปกรณ์อื่นๆ รู้ว่ามีอุปกรณ์โฆษณาพร้อมให้บริการ

นอกจากนี้ อุปกรณ์โฆษณาจะระบุว่ามีบริการใดบ้าง เราสามารถใช้ความรู้นี้ในการกรองอุปกรณ์ที่ไม่ตรงกันออก

ตัวอย่างเช่น นี่คือภาพหน้าจอของบริการที่มีอยู่ในอุปกรณ์ไทล์:

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

เมื่อสแกน เราจะตรวจสอบอีกครั้งว่าอุปกรณ์ที่เราเชื่อมต่อนั้นมี UUID ของบริการเป็น 0xfeed .

ก่อนที่เราจะเจาะลึกเข้าไปในพื้นที่ของ Bluetooth เรามาตั้งค่าแอปของเราสำหรับการดีบักโดยใช้ Logger

การบันทึก

ในบทช่วยสอนนี้ เราจะใช้ Logger ช่วยให้คุณสามารถแสดงข้อความบันทึกจากแอปของคุณโดยใช้ particle serial monitor .

หนึ่งในคุณสมบัติที่เจ๋งกว่าเกี่ยวกับตัวตัดไม้คือแนวคิดเกี่ยวกับลำดับชั้นของข้อความ ซึ่งจะช่วยให้คุณซึ่งเป็นผู้ออกแบบสามารถเลือกปิดเสียงข้อความที่อาจไม่จำเป็นได้

ตัวอย่างเช่น หากคุณมีข้อความที่ใช้สำหรับการดีบัก คุณสามารถลบออกหรือแสดงความคิดเห็นได้ หรือคุณสามารถเพิ่ม LOG_LEVEL ดังนั้นจึงถูกละเลยอย่างมีประสิทธิภาพ

นี่คือระดับการบันทึกที่มีอยู่ใน logging.h ในที่เก็บระบบปฏิบัติการของอุปกรณ์ของอนุภาค:

// Log level. Ensure log_level_name() is updated for newly added levels
typedef enum LogLevel {
    LOG_LEVEL_ALL = 1, // Log all messages
    LOG_LEVEL_TRACE = 1,
    LOG_LEVEL_INFO = 30,
    LOG_LEVEL_WARN = 40,
    LOG_LEVEL_ERROR = 50,
    LOG_LEVEL_PANIC = 60,
    LOG_LEVEL_NONE = 70, // Do not log any messages
    // Compatibility levels
    DEFAULT_LEVEL = 0,
    ALL_LEVEL = LOG_LEVEL_ALL,
    TRACE_LEVEL = LOG_LEVEL_TRACE,
    LOG_LEVEL = LOG_LEVEL_TRACE, // Deprecated
    DEBUG_LEVEL = LOG_LEVEL_TRACE, // Deprecated
    INFO_LEVEL = LOG_LEVEL_INFO,
    WARN_LEVEL = LOG_LEVEL_WARN,
    ERROR_LEVEL = LOG_LEVEL_ERROR,
    PANIC_LEVEL = LOG_LEVEL_PANIC,
    NO_LOG_LEVEL = LOG_LEVEL_NONE
} LogLevel;

เจ๋งระดับล็อก แต่เราจะใช้มันอย่างไร?

เราสามารถใช้ฟังก์ชันเหล่านี้ได้โดยเรียกใช้ฟังก์ชันใดฟังก์ชันหนึ่งต่อไปนี้:

Log.trace , Log.info , Log.warn , Log.error .

ตัวอย่างเช่น:

Log.trace("This is a TRACE message.");

หากเราตั้งค่าระดับบันทึกเป็น LOG_LEVEL_INFO เราจะเห็นเฉพาะข้อความจาก Log.info , Log.warn และ Log.error . LOG_LEVEL_WARN ? เฉพาะ Log.warn และ Log.error จะปรากฏขึ้น (หวังว่าคุณจะเข้าใจ)

ในการตั้งค่า เราจะตั้งค่าระดับเริ่มต้นเป็น LOG_LEVEL_ERROR . นอกจากนี้เรายังจะตั้งค่าเฉพาะแอป LOG_LEVEL ถึง LOG_LEVEL_TRACE . ผลลัพธ์ที่ได้ควรมีลักษณะเช่นนี้

// For logging
SerialLogHandler logHandler(115200, LOG_LEVEL_ERROR, {
    { "app", LOG_LEVEL_TRACE }, // enable all app messages
});

วิธีนี้ทำให้เราไม่ได้รับสแปมด้วยข้อความบันทึก DeviceOS นอกจากนี้ เรายังได้รับข้อความที่เกี่ยวข้องทั้งหมดจากตัวแอปเองด้วย

อย่างไรก็ตาม หากคุณต้องการตั้งค่าอุปกรณ์ของคุณเป็น LOG_LEVEL single เดียว คุณสามารถตั้งค่าได้ดังนี้:

SerialLogHandler logHandler(LOG_LEVEL_INFO);

ในขณะที่คุณเดินทางต่อไปโดยใช้ DeviceOS ของ Particle คุณจะรู้ได้ทันทีว่ามันสะดวกเพียงใด เอาล่ะ ไปต่อกันเลยดีกว่า!

การตั้งค่า

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

ก่อนอื่น เราต้องตรวจสอบให้แน่ใจว่าเราใช้ DeviceOS เวอร์ชันที่ถูกต้อง เวอร์ชันใดหลังจาก 1.3 จะมีบลูทูธ คุณสามารถรับคำแนะนำได้ที่นี่

ต่อไปเราจะต้องการเริ่มสแกนหาไทล์ เราจะต้องทำสิ่งนี้ใน loop() ทำงานในช่วงเวลาที่กำหนด เราจะใช้ millis() ตัวจับเวลาในกรณีนี้:

// Scan for devices
if( (millis() > lastSeen + TILE_RE_CHECK_MS) ){
    BLE.scan(scanResultCallback, NULL);
}

ตรวจสอบให้แน่ใจว่าคุณได้กำหนด lastSeen ที่ด้านบนของไฟล์ดังนี้:

system_tick_t lastSeen = 0;

เราจะใช้เพื่อติดตามครั้งสุดท้ายที่ "เห็น" ไทล์ เช่น เมื่อครั้งสุดท้ายที่ Argon เห็นแพ็กเก็ตโฆษณาจากไทล์

TILE_RE_CHECK_MS สามารถกำหนดเป็น

#define TILE_RE_CHECK_MS 7500

ด้วยวิธีนี้ เราจะตรวจสอบอย่างน้อยที่สุดทุกๆ 7.5 วินาทีสำหรับแพ็กเก็ตโฆษณา

ในการค้นหาอุปกรณ์ไทล์ เราจะใช้ BLE.scan . เมื่อเราเรียกมัน มันจะเริ่มกระบวนการสแกน ตามที่พบอุปกรณ์ scanResultCallback จะยิง

สำหรับตอนนี้ เราสามารถกำหนด scanResultCallback . ได้ ที่ด้านบนของไฟล์:

void scanResultCallback(const BleScanResult *scanResult, void *context) {
}

คุณสังเกตเห็นว่ามันมี BleScanResult . ซึ่งจะประกอบด้วยที่อยู่ RSSI และชื่ออุปกรณ์ (ถ้ามี) และข้อมูลบริการที่มี สิ่งนี้จะมีประโยชน์ในภายหลังเมื่อเรากำลังมองหาอุปกรณ์ไทล์ของเรา!

จำไว้ว่า BLE.scan ไม่กลับมาจนกว่าการสแกนจะเสร็จสิ้น ระยะหมดเวลาเริ่มต้นสำหรับการสแกนคือ 5 วินาที คุณสามารถเปลี่ยนค่านั้นได้โดยใช้ BLE.setScanTimeout() . setScanTimeout ใช้หน่วยเพิ่มขึ้นทีละ 10ms ดังนั้น สำหรับการหมดเวลา 500ms จะต้องมีค่า 50

สำหรับกรณีของแอปนี้ ฉันขอแนะนำให้ใช้ค่า 8 วินาที (8000 มิลลิวินาที) คุณสามารถตั้งค่าดังนี้:

BLE.setScanTimeout(800);

ในกรณีนี้ อุปกรณ์จะสแกนตราบเท่าที่ใช้ไทล์เพื่อโฆษณา วิธีนี้จึงมีโอกาสน้อยที่จะพลาดแพ็กเก็ตโฆษณา

การจัดการผลการสแกน

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

ตอนนี้เรามี scanResultCallback มากำหนดสิ่งที่เกิดขึ้นภายในกันเถอะ

อันดับแรกเราต้องการรับข้อมูลการบริการภายในข้อมูลโฆษณา วิธีที่ดีที่สุดคือการใช้ scanResult->advertisingData.serviceUUID . เราจะส่งต่ออาร์เรย์ของ UUID ว่าสิ่งใดจะถูกคัดลอกสำหรับการใช้งานของเรา

BleUuid uuids[4];
int uuidsAvail = scanResult->advertisingData.serviceUUID(uuids,sizeof(uuids)/sizeof(BleUuid));

สิ่งนี้จะเติม uuids ด้วยวิธีนี้คุณสามารถทำซ้ำได้ uuidsAvail จะเท่ากับจำนวน UUID ที่มี

ในกรณีของเรา เรากำลังมองหา UUID เฉพาะ เราจะกำหนดให้เป็นส่วนบนของไฟล์:

#define TILE_UUID 0xfeed

โดยปกติ UUID จะ มาก อีกต่อไป UUID สั้นๆ แบบนี้หมายความว่ามีการสงวนไว้หรือเป็นส่วนหนึ่งของข้อกำหนด Bluetooth ไม่ว่าในกรณีใด เราจะตรวจสอบในลักษณะเดียวกับที่เราจะตรวจสอบเวอร์ชัน 32 บิตหรือ 128 บิต

ด้วยเหตุผลในการวินิจฉัย เรายังสามารถพิมพ์ข้อมูลอุปกรณ์ได้ ในกรณีนี้ RSSI และที่อยู่ MAC ของอุปกรณ์จะสะดวก:

// Print out mac info
BleAddress addr = scanResult->address;
Log.trace("MAC: %02X:%02X:%02X:%02X:%02X:%02X", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
Log.trace("RSSI: %dBm", scanResult->rssi);

สุดท้ายมาตั้งค่าลูปเพื่อดูว่าอุปกรณ์ที่พบมี UUID หรือไม่:

// Loop over all available UUIDs
// For tile devices there should only be one
for(int i = 0; i < uuidsAvail; i++){

    // Print out the UUID we're looking for
    if( uuids[i].shorted() == TILE_UUID ) {
        Log.trace("UUID: %x", uuids[i].shorted());

        // Stop scanning
        BLE.stopScanning();

        return;
    }
}

เราสามารถเปรียบเทียบ UUID เวอร์ชัน "ย่อ" กับ TILE_UUID . ได้อย่างง่ายดาย . เป็นจำนวนเต็มอย่างง่าย จึงไม่จำเป็นต้องมีการดำเนินการเปรียบเทียบหน่วยความจำที่ซับซ้อน ดังนั้น ใช้ if( uuids[i].shorted() == TILE_UUID ) ทำงานได้ดี

คุณยังสามารถใช้ Log.trace เพื่อพิมพ์ข้อมูลการวินิจฉัย ในกรณีนี้ เราจะใช้พิมพ์ shorted() เวอร์ชันของ UUID

ทดสอบเลย!

มาทดสอบกันว่าเรามีอะไรบ้าง!

ตั้งโปรแกรมแอปให้กับ Argon ของคุณ เปิดเทอร์มินัลแล้วเรียกใช้ particle serial monitor เพื่อดูข้อความแก้ไขข้อบกพร่อง นี่คือตัวอย่างสิ่งที่คุณอาจเห็น:

0000005825 [app] TRACE: MAC: 65:C7:B3:AF:73:5C
0000005827 [app] TRACE: RSSI: -37Bm
0000005954 [app] TRACE: MAC: B3:D9:F1:F0:5D:7E
0000005955 [app] TRACE: RSSI: -62Bm
0000006069 [app] TRACE: MAC: C5:F0:74:3D:13:77
0000006071 [app] TRACE: RSSI: -62Bm
0000006217 [app] TRACE: MAC: 65:C7:B3:AF:73:5C
0000006219 [app] TRACE: RSSI: -39Bm
0000006224 [app] TRACE: MAC: B3:D9:F1:F0:5D:7E
0000006225 [app] TRACE: RSSI: -62Bm
0000006296 [app] TRACE: MAC: D7:E7:FE:0C:A5:C0
0000006298 [app] TRACE: RSSI: -60Bm
0000006299 [app] TRACE: UUID: feed

สังเกตว่าข้อความมี TRACE . อย่างไร และยัง [app] ? นั่นหมายความว่าเป็นข้อความติดตามที่มาจากรหัสแอปพลิเคชัน สะดวกใช่มั้ย

รหัสนี้ได้รับสแปมอย่างรวดเร็ว โดยเฉพาะอย่างยิ่งหากคุณอยู่ในสภาพแวดล้อมที่มีอุปกรณ์บลูทูธโฆษณาจำนวนมาก หากคุณเปิดไทล์และทำงานในที่สุด คุณจะเห็นข้อความ UUID: feed . นั่นหมายความว่า Argon ของคุณพบกระเบื้องแล้ว!

ต่อไป เราจะใช้ปุ่มโหมดออนบอร์ดเพื่อ "โปรแกรม" ที่อยู่ของไทล์ไปยังหน่วยความจำ ด้วยวิธีนี้ เราจึงสามารถกรองอุปกรณ์ทั้งหมดที่เราไม่สนใจออกไปได้

เพิ่มการกดปุ่มเปิดอุปกรณ์

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

ก่อนอื่นเราต้องหาวิธีตรวจสอบปุ่มโหมด ทางที่ดีที่สุดตามเอกสารคือใช้ System.on .

System.on(button_click, eventHandler);

อาร์กิวเมนต์แรกคือชื่อของเหตุการณ์ของระบบ ในกรณีของเราคือ button_click . อาร์กิวเมนต์ที่สองคือฟังก์ชันตัวจัดการเหตุการณ์ เราจะเรียกมันว่า eventHandler สำหรับตอนนี้

มาสร้าง eventHandler . กัน

void eventHandler(system_event_t event, int duration, void* )
{

}

สำคัญ: คุณไม่สามารถใช้ Log ฟังก์ชันภายใน eventHandler . วิธีง่ายๆ ในการทดสอบคือการสลับ LED บน D7 มาตั้งค่ากันเถอะ!

เริ่มต้น LED ใน setup()

// Set LED pin
pinMode(D7,OUTPUT);

จากนั้นเราสามารถเพิ่มสิ่งนี้ลงใน eventHandler

if( event == button_click ) {
    if( digitalRead(D7) ) {
        digitalWrite(D7,LOW);
    } else {
        digitalWrite(D7,HIGH);
    }
}

จากนั้นเราสามารถเขียนถึง D7 (ไฟ LED สีฟ้าออนบอร์ด) เรายังใช้ digitalRead . ได้ เพื่ออ่านสถานะของ LED มันจะตอบสนองด้วย HIGH หรือ LOW แล้วแต่สถานการณ์

โหลดเฟิร์มแวร์ลงในอุปกรณ์แล้วเราจะควบคุมไฟ LED สีฟ้าได้ดี!

ในส่วนถัดไป เราจะใช้ปุ่มโหมดเพื่อทำให้อุปกรณ์อยู่ในโหมด "การเรียนรู้" ซึ่งจะทำให้เราสามารถตั้งค่าด้วยการแตะเพียงครั้งเดียวกับอุปกรณ์ไทล์เป้าหมาย

กำลังจัดเก็บที่อยู่ใน EEPROM

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

ในขั้นตอนต่อไป เราจะเก็บที่อยู่ของไทล์ไว้ใน EEPROM ด้วยวิธีนี้ เมื่ออุปกรณ์ถูกรีสตาร์ทหรือหมดพลังงาน เราจะยังสามารถระบุไทล์ได้ในภายหลัง

มีคำถามหนึ่งที่เอ้อระเหยแม้ว่า เราจะเอามันไปบันทึกที่อยู่ได้อย่างไร?

โดยการตรวจสอบการกดปุ่ม เราสามารถกำหนดให้อุปกรณ์เข้าสู่โหมด "การเรียนรู้" อุปกรณ์จะสแกนหาไทล์และบันทึกที่อยู่หากพบ

ขั้นแรกให้เพิ่มเงื่อนไขภายใน if( uuids[i].shorted() == TILE_UUID ) :

// If we're in learning mode. Save to EEPROM
if( isLearningModeOn() ) {
    searchAddress = scanResult->address;
    EEPROM.put(TILE_EEPROM_ADDRESS, searchAddress);
    setLearningModeOff();
}

เราจะใช้สถานะของ D7 เพื่อให้รู้ว่าเราอยู่ใน "โหมดการเรียนรู้" เราทำได้โดยการอ่าน D7 โดยใช้ digitalRead(D7) . มาสร้างฟังก์ชันที่ทำให้ชัดเจนยิ่งขึ้นกันเถอะ:

bool isLearningModeOn() {
    return (digitalRead(D7) == HIGH);
}

นอกจากนี้เรายังสามารถแทนที่ digitalWrite(D7,LOW); และ digitalWrite(D7,HIGH); ที่มีลักษณะการทำงานที่คล้ายคลึงกัน วิธีนี้จะทำให้สิ่งที่เราทำตรงไปตรงมายิ่งขึ้น

// Set "Learning mode" on
void setLearningModeOn() {
    digitalWrite(D7,HIGH);
}

// Set "Learning mode" off
void setLearningModeOff() {
    digitalWrite(D7,LOW);
}

จากนั้นเรากำหนดตัวแปรส่วนกลาง searchAddress เป็นผลการสแกน เราตั้งค่า searchAddress แบบนี้ที่ด้านบนของไฟล์:

BleAddress searchAddress;

ต่อไปเราต้องการบันทึกลงในหน่วยความจำแบบไม่ลบเลือนโดยใช้ EEPROM.put . TILE_EEPROM_ADDRESS ถูกกำหนดเป็น 0xa . คุณสามารถกำหนด TILE_EEPROM_ADDRESS เพื่อใช้ที่อยู่หน่วยความจำใดก็ได้ที่คุณต้องการ นี่คือคำจำกัดความแบบเต็มที่ด้านบนของไฟล์

#define TILE_EEPROM_ADDRESS 0xa

สุดท้าย เราปิด LED และ "โหมดการเรียนรู้" โดยใช้ setLearningModeOff()

ทุกครั้งที่พบอุปกรณ์ เราจะใช้ millis() เพื่อตั้งค่า lastSeen . นอกจากนี้ เราสามารถติดตาม RSSI ล่าสุดได้โดยใช้ lastRSSI . เป็นวิธีที่ประหยัดในการทราบว่าอุปกรณ์อยู่ใกล้แค่ไหน เราจะใช้ scanResult->rssi เพื่อรับข้อมูลนี้และตั้งค่าเป็น lastRSSI ตัวแปร

โดยรวมแล้ว การเปลี่ยนแปลงของคุณควรมีลักษณะดังนี้:

...

// Print out the UUID we're looking for
if( uuids[i].shorted() == TILE_UUID ) {
    Log.trace("UUID: %x", uuids[i].shorted());

    // If we're in learning mode. Save to EEprom
    if( isLearningModeOn() ) {
        searchAddress = scanResult->address;
        EEPROM.put(TILE_EEPROM_ADDRESS, searchAddress);
        setLearningModeOff();
    }

    // Save info
    lastSeen = millis();
    lastRSSI = scanResult->rssi;

    // Stop scanning
    BLE.stopScanning();

    return;
}

ก่อนฟังก์ชันนี้ เราสามารถกรองอุปกรณ์ที่ไม่ตรงกับ searchAddress . ของเรา . เพิ่มสิ่งต่อไปนี้ก่อน if( uuids[i].shorted() == TILE_UUID ) :

// If device address doesn't match or we're not in "learning mode"
if( !(searchAddress == scanResult->address) && !isLearningModeOn() ) {
    return;
}

การดำเนินการนี้จะข้ามผ่านอุปกรณ์ที่ไม่ตรงกัน จะดำเนินการได้ก็ต่อเมื่อที่อยู่ตรงกันหรือเราอยู่ใน "โหมดการเรียนรู้"

ทีนี้ เพื่อให้เราโหลด searchAddress ตอนสตาร์ทเราต้องโหลดจากแฟลช เพิ่มบรรทัดนี้ใน setup(): . ของคุณ

EEPROM.get(TILE_EEPROM_ADDRESS, searchAddress);

จากนั้นตรวจสอบเพื่อให้แน่ใจว่าที่อยู่ถูกต้อง จะไม่ถูกต้องหากไบต์ทั้งหมดเป็น 0xFF :

// Warning about address
if( searchAddress == BleAddress("ff:ff:ff:ff:ff:ff") ) {
    Log.warn("Place this board into learning mode");
    Log.warn("and keep your Tile near by.");
}

เราควรจะสามารถ "สอน" Argon ของเราถึงที่อยู่ของไทล์ของเราได้ มาทดสอบกัน!

ทดสอบเลย

ตอนนี้ถ้าเราคอมไพล์และรันแอพ สังเกตว่าไม่มีเอาต์พุตบันทึกอีกแล้วหรือ เราต้อง "สอน" ที่อยู่ไทล์ให้กับอุปกรณ์อนุภาค ดังนั้นให้กดปุ่มโหมด ไฟ LED สีฟ้าควรเปิดขึ้น

เมื่อพบไทล์ของคุณแล้ว ไฟ LED จะดับลง และคุณจะเห็นผลลัพธ์บางอย่างในบรรทัดคำสั่ง คล้ายกับที่เราเคยเห็นมาก่อน:

0000006296 [app] TRACE: MAC: D7:E7:FE:0C:A5:C0
0000006298 [app] TRACE: RSSI: -60Bm
0000006299 [app] TRACE: UUID: feed

อุปกรณ์ได้รับการผูกมัดกับหน่วยความจำแล้ว!

คุณยังตรวจสอบได้ด้วยว่าระบบยังบันทึกอยู่หลังจากรีเซ็ตแล้วหรือไม่ กด รีเซ็ต ปุ่มและตรวจสอบเอาต์พุตเดียวกันกับด้านบน ถ้ามันขึ้นแสดงว่าเรายังดีอยู่!

อัปเดตระบบคลาวด์

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

สุดท้ายมาตั้งค่าฟังก์ชันที่ชื่อว่า checkTileStateChanged . เราจะใช้เพื่อตรวจสอบการเปลี่ยนแปลงสถานะของไทล์ในช่วงเวลาปกติ

bool checkTileStateChanged( TilePresenceType *presence ) {

}

จุดประสงค์หลักของฟังก์ชันนี้คือการเปรียบเทียบ lastSeen ตัวแปรที่มีระยะเวลา "หมดเวลา" ในกรณีของเรา ระยะเวลาหมดเวลาของเราคือ TILE_NOT_HERE_MS ซึ่งควรตั้งค่าเป็น

#define TILE_NOT_HERE_MS 30000

ใกล้ด้านบนสุดของโปรแกรมของคุณ ยังมีอีกสองเงื่อนไขที่ต้องค้นหา ที่แห่งหนึ่ง lastSeen เท่ากับ 0 ซึ่งโดยปกติเนื่องจากแอปยังไม่พบไทล์หลังจากเริ่มต้น

กรณีสุดท้ายคงเป็นกรณีที่มีคนเห็นเครื่องแล้วและ lastSeen ไม่ใช่ 0 ดังนั้นภายใน checkTileStateChanged มารวมทุกอย่างเข้าด้วยกัน

// Check to see if it's here.
if( millis() > lastSeen+TILE_NOT_HERE_MS ) {

} else if ( lastSeen == 0 ) {

} else {

}

return false;

ตอนนี้เราต้องการให้ฟังก์ชันนี้คืนค่าเป็นจริง หากสถานะมีการเปลี่ยนแปลง . เราจึงต้องใช้ประโยชน์จาก TilePresenceType ตัวชี้ในข้อตกลง

TilePresenceType เป็นเพียงการแจงนับสถานะที่เป็นไปได้ทั้งหมด คุณสามารถติดไว้ที่ด้านบนของไฟล์ได้เช่นกัน นี่คือ:

typedef enum {
    PresenceUnknown,
    Here,
    NotHere
} TilePresenceType;

คุณจะต้องมีตัวแปรส่วนกลางที่เราสามารถส่งผ่านไปยังฟังก์ชันได้ ตั้งค่านี้ที่ด้านบนสุดของไฟล์ด้วย:

// Default status
TilePresenceType present = PresenceUnknown;

ตอนนี้เราสามารถเปรียบเทียบในแต่ละขั้นตอนได้ เป็นไปตามเกณฑ์หรือไม่? รัฐแตกต่างจากที่แล้วหรือไม่? หากเป็นเช่นนั้น ให้คืนค่าเป็น จริง

จำไว้ว่าเราต้องการตั้งค่า presence เป็นค่าที่ปรับปรุงใหม่ ดังนั้นแต่ละเงื่อนไขควรอัปเดตค่าการมีอยู่ ตัวอย่างเช่น:

*presence = NotHere;

นี่คือหน้าตาของฟังก์ชันที่ล้างออกอย่างสมบูรณ์:

bool checkTileStateChanged( TilePresenceType *presence ) {

    // Check to see if it's here.
    if( millis() > lastSeen+TILE_NOT_HERE_MS ) {
        if( *presence != NotHere ) {
            *presence = NotHere;
            Log.trace("not here!");
            return true;
        }
    // Case if we've just started up
    } else if ( lastSeen == 0 ) {
        if( *presence != PresenceUnknown ) {
            *presence = PresenceUnknown;
            Log.trace("unknown!");
            return true;
        }
    // Case if lastSeen is < TILE_NOT_HERE_MS
    } else {
        if( *presence != Here ) {
            *presence = Here;
            Log.trace("here!");
            return true;
        }
    }

    return false;
}

ขณะนี้สามารถใช้ฟังก์ชันนี้ในลูปหลักใต้ตัวจับเวลาเพื่อเริ่ม Ble.scan() . เราสามารถใช้เพื่อส่งเพย์โหลด JSON ในกรณีนี้ เราจะรวมข้อมูลสำคัญ เช่น ที่อยู่บลูทูธ lastSeen ข้อมูล lastRSSI ข้อมูลและข้อความ

// If we have a change
if( checkTileStateChanged(&present) ) {

}

เราจะใช้อาร์เรย์ของ char เพื่อรับที่อยู่ของเราในรูปแบบสตริง โยงกันได้ toString() ด้วย toCharArray เพื่อให้ได้สิ่งที่เราต้องการ

// Get the address string
char address[18];
searchAddress.toString().toCharArray(address,sizeof(address));

ตัวอย่างสตริงเพย์โหลดอาจมีลักษณะดังนี้:

// Create payload
status = String::format("{\"address\":\"%s\",\"lastSeen\":%d,\"lastRSSI\":%i,\"status\":\"%s\"}",
    address, lastSeen, lastRSSI, messages[present]);

status เป็นเพียงสตริงที่กำหนดไว้ที่ด้านบนของไฟล์:

// The payload going to the cloud
String status;

คุณสังเกตเห็นว่ามีตัวแปรชื่อ messages . ด้วย . นี่คืออาร์เรย์ const แบบคงที่ของสตริง มีการแมปกับค่าจาก TilePresenceType . นี่คือลักษณะที่ปรากฏ

const char * messages[] {
    "unknown",
    "here",
    "not here"
};

ทางนั้น PresenceUnknown ตรงกับ "unknown" , Here ตรงกับ "here" ฯลฯ เป็นวิธีที่ง่ายในการเชื่อมโยงสตริงกับ enum

สุดท้าย เราจะเผยแพร่และดำเนินการ ซึ่งช่วยให้เราส่งข้อมูลอัปเดตได้ทันที

// Publish the RSSI and Device Info
Particle.publish("status", status, PRIVATE, WITH_ACK);

// Process the publish event immediately
Particle.process();

ฟังก์ชันโดยรวมควรมีลักษณะดังนี้:

// If we have a change
if( checkTileStateChanged(&present) ) {

    // Get the address string
    char address[18];
    searchAddress.toString().toCharArray(address,sizeof(address));

    // Create payload
    status = String::format("{\"address\":\"%s\",\"lastSeen\":%d,\"lastRSSI\":%i,\"status\":\"%s\"}",
        address, lastSeen, lastRSSI, messages[present]);

    // Publish the RSSI and Device Info
    Particle.publish("status", status, PRIVATE, WITH_ACK);

    // Process the publish event immediately
    Particle.process();

}

มาทดสอบกันเลย!

กำลังทดสอบ!

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

เราสามารถทดสอบเพื่อให้แน่ใจว่าเหตุการณ์ Publish ของเราเกิดขึ้นโดยที่เหตุการณ์ไม่ออกจาก Particle Workbench เปิดเทอร์มินัลใหม่โดยไปที่ ดู → เทอร์มินัล จากนั้นใช้คำสั่งต่อไปนี้:

particle subscribe --device <device_name> <event_name>

แทนที่ <device_name> ด้วยชื่อหรือ ID ของอุปกรณ์ของคุณ

แทนที่ <event_name> กับชื่องาน ในกรณีของเราคือ status .

จากนั้นคุณสามารถทดสอบได้ทั้งหมดโดยถอดแบตเตอรี่ออกและรอการแจ้งเตือน "ไม่อยู่ที่นี่" เสียบแบตเตอรี่กลับเข้าไปและคุณควรได้รับการแจ้งเตือน "ที่นี่"

นี่คือตัวอย่างผลลัพธ์

> particle subscribe --device hamster_turkey status

Subscribing to "status" from hamster_turkey's stream
Listening to: /v1/devices/hamster_turkey/events/status
{"name":"status","data":"{\"address\":\"C0:A5:0C:FE:E7:D7\",\"lastSeen\":40154002,\"lastRSSI\":-82,\"status\":\"not here\"}","ttl":60,"published_at":"2019-09-07T02:29:42.232Z","coreid":"e00fce68d36c42ef433428eb"}
{"name":"status","data":"{\"address\":\"C0:A5:0C:FE:E7:D7\",\"lastSeen\":40193547,\"lastRSSI\":-83,\"status\":\"here\"}","ttl":60,"published_at":"2019-09-07T02:29:50.352Z","coreid":"e00fce68d36c42ef433428eb"}

การกำหนดค่าเว็บฮุค

ในส่วนสุดท้ายของบทช่วยสอนนี้ เราจะตั้งค่าการแจ้งเตือนแบบพุชโดยใช้เว็บฮุค ดังที่ได้กล่าวไว้ก่อนหน้านี้ เราจะใช้ Pushover และ API ที่มีประโยชน์เพื่อส่งการแจ้งเตือนแบบพุชไปยังอุปกรณ์ที่คุณเลือก

Pushover มี API ที่ใช้งานง่ายอย่างน่าอัศจรรย์ แอปพลิเคชันของพวกเขาคือมีดทหารสวิสสำหรับสถานการณ์ที่คุณไม่ต้องการเข้ารหัสแอปเพื่อส่งการแจ้งเตือนแบบพุช

สิ่งแรกที่คุณต้องจำไว้คือรหัสผู้ใช้ คุณสามารถรับสิ่งนั้นได้โดยลงชื่อเข้าใช้ Pushover หมายเหตุ:คุณจะต้องสร้างบัญชีก่อนหากยังไม่ได้ทำ

ควรมีลักษณะดังนี้:

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

หากคุณเข้าสู่ระบบแล้วไม่เห็นหน้านี้ ให้คลิกที่โลโก้ Pushover และนั่นควรนำคุณกลับมา

ต่อไปเราจะต้องการสร้างแอปพลิเคชัน คลิกที่ แอปและปลั๊กอิน ที่ด้านบนของหน้าจอ

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

จากนั้นคุณควรคลิก สร้างแอปพลิเคชันใหม่ ซึ่งจะทำให้เราได้รับ API Token ที่จำเป็นในการตั้งค่า Particle Webhook

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

ตั้งชื่อตามที่เห็นสมควร กรอกรายละเอียดหากต้องการเตือนความจำ คลิกที่ช่อง แล้วคลิก สร้างแอปพลิเคชัน

คุณควรไปที่หน้าถัดไป คัดลอกและบันทึก โทเค็น/คีย์ API เราต้องการสิ่งนี้ในไม่กี่ขั้นตอนด้วย

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

ตอนนี้ มาตั้งค่า Webhook กัน ข้ามไปที่ https://console.particle.io และสร้างการผสานรวมใหม่

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

เราจะตั้งค่า ชื่อกิจกรรม เป็น สถานะ .

URL ถึง https://api.pushover.net/1/messages.json

นอกจากนี้ หากคุณต้องการกรองตามอุปกรณ์เฉพาะ ให้เลือกใน อุปกรณ์แบบเลื่อนลง

ภายใต้ การตั้งค่าขั้นสูง เราจะเสร็จสิ้นด้วยการตั้งค่าสองสามฟิลด์

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

สร้างฟิลด์ต่อไปนี้:โทเค็น ผู้ใช้ , ชื่อเรื่อง และ ข้อความ . จากนั้นตั้งค่าโทเค็นเป็น โทเค็น API เราได้รับก่อนหน้านี้ ทำเช่นเดียวกันสำหรับ รหัสผู้ใช้

ชื่อเรื่อง จะแสดงเป็นชื่อข้อความของคุณ ทำให้ทุกอย่างเหมาะสมสำหรับคุณ

คุณสามารถตั้งค่าข้อความ เป็น The Tile is currently {{{status}}}. RSSI: {{{lastRSSI}}} .

เรากำลังใช้แม่แบบหนวดที่นี่ อนุญาตให้คุณใช้ข้อมูลในเพย์โหลดที่เผยแพร่และฟอร์แมตใหม่ตามที่คุณต้องการ ในกรณีของเรา เราใช้พวกเขาเพื่อ "เติมในช่องว่าง" ข้อความ เมื่อประมวลผลแล้วจะมีลักษณะดังนี้:

The Tile is currently here. RSSI: -77

ฉันจะพูดถึงเทมเพลตเหล่านี้เพิ่มเติมในคำแนะนำของฉัน โปรดคอยติดตาม!

ทดสอบเลย

เมื่อผสานรวมเรียบร้อยแล้ว คุณสามารถทดสอบสิ่งที่เราทำในขั้นตอนก่อนหน้านี้ได้ ถอดแบตเตอรี่ออกและรอข้อความ "not here" ใส่กลับแล้วรอข้อความ "ที่นี่"

iPhone จะมีลักษณะดังนี้:

วิธีใช้อนุภาคอาร์กอนสำหรับการติดตามตำแหน่ง

อย่างที่คุณเห็นฉันทดสอบมันเป็นกลุ่ม! ?

ถ้าคุณมาไกลถึงขนาดนี้แล้วและทุกอย่างทำงานได้ดี เยี่ยมมาก ตอนนี้คุณมีตัวติดตามไทล์สำหรับบ้าน สำนักงาน หรือที่ใดก็ตาม

เดอะโค้ด

กำลังมองหาโค้ดสำเร็จรูปสำหรับตัวอย่างนี้หรือไม่? ฉันก็เหมือนกัน! มันโฮสต์บน Github และสามารถใช้ได้ที่นี่

บทสรุป

อย่างที่คุณจินตนาการ เทคนิคและเทคโนโลยีที่ใช้ในบทความนี้สามารถใช้ได้หลายวิธี มาสรุปประเด็นสำคัญบางส่วนกัน:

  1. การใช้ Bluetooth Central เพื่อสแกนหาและระบุอุปกรณ์ Tile ที่มีจำหน่ายในท้องตลาด
  2. การจัดเก็บข้อมูลการระบุไทล์ไปยัง EEPROM ด้วยวิธีนี้จะสามารถดึงข้อมูลได้เมื่อเริ่มต้นใช้งาน
  3. การใช้ Particle.publish . ที่คุ้นเคยของเรา เพื่อผลักดันการอัปเดตไปยังระบบคลาวด์
  4. การใช้ Particle Integration Webhook เพื่อสร้างการแจ้งเตือนแบบพุชเกี่ยวกับการเปลี่ยนแปลงสถานะ

ตอนนี้คุณใช้งานได้แล้ว ขยายขอบเขต แฮ็กและทำให้เป็นของคุณ โอ้และอย่าลืมแบ่งปัน! ฉันชอบที่จะได้ยินจากคุณ hello@jaredwolff.com

ชอบโพสต์นี้? คลิกลิงก์แชร์ด้านล่างและแชร์กับคนทั้งโลก :)

นี่เป็นการข้ามโพสต์จากบล็อกของฉัน คุณสามารถตรวจสอบต้นฉบับได้ที่นี่

สนใจเรียนรู้เพิ่มเติม? ฉันกำลังเขียนคู่มือเกี่ยวกับวิธีการใช้ประโยชน์สูงสุดจากแพลตฟอร์มอนุภาค เรียนรู้เพิ่มเติมได้ที่นี่