কখনো প্রজেক্টে প্রেজেন্স বা লোকেশন ট্র্যাকিং যোগ করতে চেয়েছেন? সমাধান না পেয়ে খלחמתে থাকছেন?
চিন্তা করবেন না, আপনি একলভাবে নন!
এই পোস্টে আপনি একটি বেসিক ট্র্যাকিং এবং নোটিফিকেশন অ্যাপ্লিকেশন ইমপ্লিমেন্ট করতে শিখবেন। আমরা Particle Argon এবং Tile Mate ব্যবহার করব।
শেষে আপনি বলতে পারবেন Tile প্রেজেন্ট কিনা। প্লাস Pushover ব্যবহার করে আপনার পছন্দের ডিভাইসগুলোর পুশ নোটিফিকেশন পাঠাব।
চলুন শুরু করি!
নোট শুরু করার আগে, এই পোস্ট বড়। আপনি PDF ভার্সন ডাউনলোড করে রাখতে পারেন।
প্রাথমিক তদন্ত
Tile ব্যবহার করার আইডিয়া প্রথমে স্পষ্ট নয়। আদর্শভাবে ফোন ব্যবহার করাটা বেশি স্মার্ট লাগতো। দুর্ভাগ্যবশত, এটা ভাবলemate অপশন নয়। এতে আরও রিসার্চ এবং ব্লুটুথ iOS অ্যাপ তৈরি করা লাগত।
তাই ফোন ব্যবহার করার আইডিয়া বাদ দিলাম।
তারপর ভাবলাম, "কোন ডিভাইসগুলো সবসময় অ্যাডভার্টাইজ করে?"
এটাই আমাকে Tile-এর মতো ট্র্যাকারের দিকে নিয়ে গেলো।
Tile হাতে পাওয়ার পর কিছু টেস্ট করা হয়। প্রথমে, Tile অ্যাপ্লিকেশন।
ডিভাইস কানেক্ট এবং ব্যবহার করতে পারলাম। এটা একটি ক্যাচি টিউন বাজাতে পারলাম। 😊
তারপর একটি ব্লুটুথ স্ক্যানার অ্যাপ ব্যবহার করলাম। সব রেজাল্ট স্ক্রোল করলাম এবং বাঙো! Tile পাওয়া গেলো।
কয়েক ঘন্টা পর আবার চেক করলাম। নিশ্চিত হতে চেয়েছিলাম কিছু সময় পর সেটা স্লিপ মোডে চলে যায় কিনা। বের হয়, সেটা সবসময় অ্যাডভার্টাইজ করে। আমার জানা মতে প্রায় ৮ সেকেন্ড পর পর।
সকল টেস্ট থেকে একই kết luận: এটি প্রেজেন্স ডিটেকশনের জন্য সহজে ব্যবহার করা যায়।
পরবর্তী ধাপ: Argon-এ কিভাবে কাজ করানো যায় সেটা বের করা।
অ্যাডভার্টাইজিং
পূর্বের ধাপ থেকে আমরা জানি Tile প্রায় ৮ সেকেন্ড পর পর অ্যাডভার্টাইজ করে। তাই Argon, Xenon বা Boron সহ যেকোনো ডিভাইস দিয়ে এটি স্ক্যান করা সহজ।
এই উদাহরণের জন্য Argon ব্যবহার করার পরামর্শ দিচ্ছি। কারণ ব্লুটুথ এবং মেশ একই রেডিও শেয়ার করে। Tile স্ক্যান করার সময়, মেশে কানেক্টেড Xenon প্রায়শই অ্যাডভার্টাইজিং প্যাকেট মিস করতো। এতে ফালস 네가েটিভ (এবং হ مستشفى!) হতো।
একই লজিক অনুযায়ী, নিশ্চিত করুন আপনার Argon কোনো মেশ নেটওয়ার্কে কানেক্টেড নয়। CLI ব্যবহার করে রিমুভ করতে পারেন। ডিভাইস কম্পিউটারের সাথে কানেক্ট করুন এবং নিচের কমান্ড রান করুন:
particle mesh remove <device name/ID>
<device name/ID> আপনার ডিভাইসের নাম বা আইডি দিয়ে রিপ্লেস করুন।
চলুন মূল কাজে ফিরি।
ব্লুটুথে অ্যাডভার্টাইজিং-এর কিছু উদ্দেশ্য থাকতে পারে। সাধারণত এটি পেয়ারিং ফেজের শুরু নির্দেশ করে। এভাবে অন্য ডিভাইসগুলো জানতে পারে যে অ্যাডভার্টাইজিং ডিভাইসটি উপলব্ধ।
এছাড়া অ্যাডভার্টাইজিং ডিভাইস বোঝায় সেটার কী কী সার্ভিস রয়েছে। আমরা এই জ্ঞান ব্যবহার করে मैच না করার ডিভাইসগুলো ফিল্টার আউট করতে পারি।
উদাহরণস্বরূপ, Tile ডিভাইসে উপলব্ধ সার্ভিসগুলোর স্ক্রিনশট:
স্ক্যান করার সময় আমরা চেক করব ডিভাইসটির সার্ভিস UUID 0xfeed কিনা।
ব্লুটুথে গভীরে যাওয়ার আগে, Logger ব্যবহার করে ডিবাগিং সেটআপ করি।
লগিং
এই টিউটোরিয়ালে আমরা Logger ব্যবহার করব। এটি particle serial monitor ব্যবহার করে অ্যাপ থেকে লগ মেসেজ ডিসপ্লে করতে দেয়।
Logger-এর একটি কুল ফিচার হলো মেসেজ হায়ারার্কি। এটি ডিজাইনারের हाथে দেয় যাতে অপ্রয়োজনীয় মেসেজ সিলেক্টিভলি 뮤ট করা যায়।
উদাহরণস্বরূপ, ডিবাগিং용 মেসেজ থাকলে। আপনি সেগুলো রিমুভ বা কমেন্ট আউট করতে পারেন। অথবা LOG_LEVEL বাড়িয়ে সেগুলো ইফেক্টিভলি ইগনোর করতে পারেন।
Particle-এর device-os রিপোজিটরির 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 লগ মেসেজে স্প্যাম হবেনা। প্লাস, অ্যাপের সকল প্রযোজ্য মেসেজ পাওয়া যাবে।
বuseinanders, আপনি যদি ডিভাইসকে একক LOG_LEVEL-এ সেট করতে চান, তবে এমন করুন:
SerialLogHandler logHandler(LOG_LEVEL_INFO);
Particle DeviceOS ব্যবহার করতে থাকলে আপনি শীঘ্রই বুঝবেন কতটা হ্যান্ডি এটি। চলুন মূল কাজে যাই!
সেটআপ করা
প্রথমে, সঠিক DeviceOS ভার্সন ব্যবহার করছি কিনা নিশ্চিত করুন। ১.৩ এর পরে যেকোনো ভার্সনে ব্লুটুথ রয়েছে। নির্দেশনা এখানে পাবেন।
পরবর্তী, Tile-র জন্য স্ক্যান শুরু করব। loop() ফাংশনে নির্দিষ্ট ইন্টারভালে এটি করব। millis() টাইমার ব্যবহার করব:
// Scan for devices
if( (millis() > lastSeen + TILE_RE_CHECK_MS) ){
BLE.scan(scanResultCallback, NULL);
}
lastSeen ফাইলের শীর্ষে ডিফাইন করুন:
system_tick_t lastSeen = 0;
এটি ট্র্যাক করবে Tile শেষবার কখন "দেখা" পয়েছে। অর্থাৎ Argon Tile-র অ্যাডভার্টাইজিং প্যাকেট শেষবার কখন দেখল।
TILE_RE_CHECK_MS ডিফাইন করুন:
#define TILE_RE_CHECK_MS 7500
এইভাবে আমরা কমপক্ষে ৭.৫ সেকেন্ড পর পর অ্যাডভার্টাইজিং প্যাকেট চেক করব।
Tile ডিভাইস খুঁজতে BLE.scan ব্যবহার করব। কল করলে স্ক্যানিং শুরু হয়। ডিভাইস পাওয়া যেতে scanResultCallback ফায়ার হবে।
এখন scanResultCallback ফাইলের শীর্ষে ডিফাইন করুন:
void scanResultCallback(const BleScanResult *scanResult, void *context) {
}
এতে BleScanResult থাকে। এতে অ্যাড্রেস, RSSI, ডিভাইস নেম (যদি থাকে), এবং উপলব্ধ সার্ভিস ইনফো থাকে। Tile ডিভাইস খুঁজার সময় এটা খুব কাজে লাগবে!
মনে রাখবেন, BLE.scan স্ক্যানিং কমপ্লিট হওয়া পর্যন্ত রিটার্ন করে না। ডিফল্ট টাইমআউট ৫ সেকেন্ড। BLE.setScanTimeout() দিয়ে চেঞ্জ করা যায়। setScanTimeout ১০ms ইনক্রিমেন্টে মান নেয়। ৫০০ms টাইমআউটের জন্য ৫০ মান দরকার।
এই অ্যাপের জন্য, আমি ৮s (৮০০০ms) রিকমেন্ড করছি। এমন করুন:
BLE.setScanTimeout(800);
এই কেসে, ডিভাইস Tile অ্যাডভার্টাইজ করার পর্যন্ত স্ক্যান করবে। এভাবে অ্যাডভার্টাইজিং প্যাকেট মিস হওয়ার চান্স কম।
স্ক্যান রেজাল্ট হ্যান্ডলিং
এখন 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 মানে এটি রিজার্ভড বা ব্লুটুথ স্পেসিফিকেশনের অংশ। যাহোক, আমরা একইভাবে চেক করব ৩২-বিট বা ১২৮-বিট ভার্সনের মতো।
ডায়াগনোস্টিক কারণাবশত ডিভাইস ইনফো প্রিন্ট করব। এখানে 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
// Tile ডিভাইসের জন্য শুধু একটি হওয়া উচিত
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;
}
}
shorted() 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] থাকতে থাকতে বোঝা যায় এটা অ্যাপ কোড থেকে আসা ট্রেস মেসেজ। হ্যান্ডি, ঠিক না?
এই কোড দ্রুত স্প্যামি হয়ে যায়, বিশেষ করে যদি আপনার চারপাশে অনেক অ্যাডভার্টাইজিং ব্লুটুথ ডিভাইস থাকে। আপনার Tile অন থাকলে végül UUID: feed মেসেজ দেখবেন। অর্থাৎ আপনার Argon Tile খুঁজে পেয়েছে!
পরবর্তী ধাপে, অনবোর্ড মোড বাটন ব্যবহার করে Tile-এর অ্যাড্রেস মেমরিতে "প্রোগ্রাম" করব। এভাবে আমরা যেসব ডিভাইসকে চিনতে পারি না সেগুলো ফিল্টার আউট করতে পারব।
বাটন পুশে ডিভাইস যোগ করুন
প্রথমে মোড বাটন মনিটর কীভাবে করবেন সেটা বের করতে হবে। ডকুমেন্টেশন অনুযায়ী সেরা উপায় System.on।
System.on(button_click, eventHandler);
প্রথম আর্গুমেন্ট সিস্টেম ইভেন্টের নাম। আমাদের কেসে button_click। দ্বিতীয় আর্গুমেন্ট একটি ইভেন্ট হ্যান্ডলার ফাংশন। এটাকে eventHandler বলব।
এখন eventHandler তৈরি করি:
void eventHandler(system_event_t event, int duration, void* ) {
}
গুরুত্বপূর্ণ: eventHandler-এর ভিতরে Log ফাংশন ব্যবহার করা যায় না। টেস্ট করার সহজ উপায়: D7-এ লেড টগল করুন। সেটআপ করি!
setup()-এ LED ইনিশিয়ালাইজ করুন:
// 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-র উপর সুন্দর কন্ট্রোল হবে!
পরবর্তী সেকশনে, মোড বাটন ব্যবহার করে ডিভাইসকে "লার্নিং" মোডে নিয়ে যাব। এতে টার্গেট Tile ডিভাইসের সাথে वन-টাচ সেটআপ করা যাবে।
EEPROM-এ অ্যাড্রেস স্টোর করা
এই ধাপে Tile-এর অ্যাড্রেস EEPROM-এ স্টোর করব। এভাবে ডিভাইস রিস্টার্ট বা পাওয়ার লস হলেও Tile পরে আইডেন্টিফাই করা যাবে।
একটা প্রশ্ন বাকি: প্রথমে অ্যাড্রেস সেভ কীভাবে করব?
বাটন প্রেস মনিটর করে ডিভাইসকে "লার্নিং" মোডে নিয়ে যাব। ডিভাইস Tile স্ক্যান করবে এবং পেলে অ্যাড্রেস সেভ করবে।
প্রথমে 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-এর স্ট্যাটাস ব্যবহার করে "লার্নিং মোড" জানব। digitalRead(D7) দিয়ে 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
শেষে, setLearningModeOff() দিয়ে LED এবং "লার্নিং মোড" অফ করব।
প্রতিবার ডিভাইস পাওয়া যেতে 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-কে Tile-এর অ্যাড্রেস "শিখাতে" পারব। টেস্ট করি!
টেস্ট করুন।
এখন অ্যাপ কম্পাইল ও রান করলে লগ আউটপুট থাকবে না। Tile-এর অ্যাড্রেস Argon-এ "শিখাতে" হবে। মোড বাটন চাপুন। ব্লু LED অন হবে।
Tile পাওয়া যেতে LED অফ হবে এবং কমান্ড লাইনে আউটপুট দেখাবে। পূর্বের মতো:
0000006296 [app] TRACE: MAC: D7:E7:FE:0C:A5:C0
0000006298 [app] TRACE: RSSI: -60Bm
0000006299 [app] TRACE: UUID: feed
ডিভাইস মেমরিতে কমিট হয়েছে!
রিসেট বাটন চাপে রিসেট করার পর একই আউটপুট আসে কিনা চেক করুন। আসলে, আমরা এখনও ভালো আছি!
ক্লাউড আপডেট করুন
শেষে checkTileStateChanged ফাংশন সেটআপ করব। রেগুলার ইন্টারভালে Tile-এর স্টেট চেঞ্জ চেক করার জন্য।
bool checkTileStateChanged( TilePresenceType *presence ) {
}
এই ফাংশনের মূল উদ্দেশ্য lastSeen ভেরিয়েবলকে "টাইমআউট" ডুরেশন সাথে কম্পেয়ার করা। আমাদের কেসে টাইমআউট TILE_NOT_HERE_MS যা সেট থাকবে:
#define TILE_NOT_HERE_MS 30000
প্রোগ্রামের শীর্ষে। আরও দুটি কন্ডিশন আছে। একটি lastSeen == 0—সাধারণত স্টার্টআপের পর Tile এখনো না পাওয়ায়। অন্যটি ডিভাইস দেখা পাওয়ায় এবং lastSeen != 0। checkTileStateChanged-এ সব মিলিয়ে দি:
// Check to see if it's here.
if( millis() > lastSeen+TILE_NOT_HERE_MS ) {
} else if ( lastSeen == 0 ) {
} else {
}
return false;
এই ফাংশন শুধু স্টেট চেঞ্জ হলে true রিটার্ন করবে। তাই TilePresenceType পয়েন্টার অ্যাডভান্টেজ নেব।
TilePresenceType হলো সব সম্ভাব্য স্টেটের একটি এনুমেরেশন। ফাইল শীর্ষে রাখুন:
typedef enum {
PresenceUnknown,
Here,
NotHere
} TilePresenceType;
ফাংশনে পাস করার জন্য একটি গ্লোবাল ভেরিয়েবল দরকার। ফাইল শীর্ষে সেট করুন:
// Default status
TilePresenceType present = PresenceUnknown;
এখন প্রতিটি স্টেজে কম্পেয়ার করব। ক্যারেন্টের সাথে আলাদা কিনা? হলে true রিটার্ন। 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 < 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 হলো ফাইল শীর্ষে ডিফাইন করা একটি String:
// The payload going to the cloud
String status;
messages ভেরিয়েবলও আছে। এটি একটি স্ট্যাটিক কনস্ট অ্যারে স্ট্রিং। TilePresenceType-এর ভ্যালুগুলোর সাথে ম্যাপ করা। দেখতে এমন:
const char * messages[] { "unknown", "here", "not here" };
এভাবে PresenceUnknown → "unknown", Here → "here"। এনামের সাথে স্ট্রিং অ্যাসোসিয়েট করার সস্তা সহজ উপায়।
শেষে পাবলিশ এবং প্রসেস। ইমিডিয়েটলি আপডেট পাঠাতে।
// 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();
}
এখন টেস্ট করি!
টেস্টিং!
Particle Workbench ছাড়া পাবলিশ ইভেন্ট চেক করতে পারবেন। View → Terminal-এ যেন নতুন টার্মিনাল খুলুন। কমান্ড:
particle subscribe --device <device_name> <event_name>
<device_name> আপনার ডিভাইসের নাম বা আইডি দিয়ে রিপ্লেস করুন।
<event_name> ইভেন্টের নাম দিয়ে রিপ্লেস করুন। আমাদের কেসে status।
ব্যাটারি খুলে "not here" অ্যালার্টের জন্য অপেক্ষা করুন। ব্যাটারি ফিরিয়ে দিয়ে "here" অ্যালার্টের জন্য অপেক্ষা করুন।
আউটপুট উদাহরণ:
> 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 খুব সহজ। তাদের অ্যাপ পুশ নোটিফিকেশন পাঠানোর জন্য একটি সুইস আর্মি নাইফ যেখানে অ্যাপ কোড করতে চান না।
প্রথমে আপনার user key নোট করুন। Pushover-এ লগ ইন করলে পাবেন। নোট: একাউন্ট না থাকলে আগে সেটআপ করুন।
একদম এরকম দেখাবে:
লগ ইন থাকলে এই পেজ না দেখলে, Pushover লোগো-এ ক্লিক করলে ফিরে আসবে।
পরবর্তী, একটি অ্যাপ্লিকেশন তৈরি করুন। স্ক্রিনের শীর্ষে Apps & Plugins-এ ক্লিক করুন।
Create a New Application-এ ক্লিক করুন। এতে API Token পাওয়া যাবে যা Particle Webhook সেটআপে লাগবে।
নাম আপনার পছন্দমতো দিন। ডেসক্রিপশন দিলে মনে রাখতে সুবিধা। বক্সে টিক দিন এবং Create Application-এ ক্লিক করুন।
পরবর্তী পেজে যাবে। API Token/Key কপি করে সেভ করুন, কিছুক্ষণ পর লাগবে।
এখন ওয়েবহুক সেটআপ। https://console.particle.io-এ যান এবং একটি নতুন ইন্টিগ্রেশন তৈরি করুন।
Event Name-কে status সেট করুন।
URL-কে https://api.pushover.net/1/messages.json সেট করুন।
নির্দিষ্ট ডিভাইস ফিল্টার করতে চাইলে Device dropdown-এ সিলেক্ট করুন।
Advanced Settings-এ কিছু ফিল্ড সেট করব।
নিচের ফিল্ডগুলো তৈরি করুন: token, user, title, এবং message। token-এ আগের API Token বসান। user-এ User Key বসান।
title আপনার মেসেজের টাইটেল হিসেবে দেখাবে। আপনার পছন্দমতো কিছু দিন।
message-কে The Tile is currently {{{status}}}. RSSI: {{{lastRSSI}}} সেট করুন।
মাস্ট্যাচ টেমপ্লেট ব্যবহার করছি। এগুলো পাবলিশড পেলোডের ডেটা ব্যবহার করে রিফরম্যাট করতে দেয়। আমাদের কেসে "খালি জায়গা পূরণ" করছে। প্রসেস হওয়ার পর মেসেজ এমন দেখাবে:
The Tile is currently here. RSSI: -77
সাইড নোট: এই টেমপ্লেটগুলো আমার গাইডে আরও আলোচনা করব। সতর্ক থাকুন!
টেস্ট করুন
ইন্টিগ্রেশন প্লেস হলে, আগের ধাপের মতো টেস্ট করুন। ব্যাটারি খুলুন, "not here" মেসেজের জন্য অপেক্ষা করুন। ফিরিয়ে দিন, "here" মেসেজের জন্য অপেক্ষা করুন।
iPhone-এ এরকম দেখাবে:
আমি কয়েকবার টেস্ট করলাম! 😊
এতদূর এসে সব কাজ করলে, গরেট ওয়ার্ক! এখন আপনার বাড়ি, অফিস বা যেকোনো জায়গার জন্য Tile ট্র্যাকার রেডি।
কোড
এই উদাহরণের ফাইনাল কোড খুঁজছেন? আমিও খুঁজতাম! GitHub-এ হোস্ট করা, এখানে উপলব্ধ।
উপসংহার
এই আর্টিকেলে ব্যবহৃত টেকনিক এবং টেকনোলজি অনেকভাবে ব্যবহার করা যায়। মূল টেকওয়ে যাব:
- ব্লুটুথ সেন্ট্রাল ব্যবহার করে অফ-দ-শেলফ Tile ডিভাইস স্ক্যান ও আইডেন্টিফাই করা
- Tile-এর আইডেন্টিফাইং ইনফো EEPROM-এ স্টোর করা যাতে স্টার্টআপে রিট্রিভ করা যায়
- পরিচিত
Particle.publishব্যবহার করে ক্লাউডে আপডেট পুশ করা - Particle Integration Webhook ব্যবহার করে স্টেট চেঞ্জে পুশ নোটিফিকেশন তৈরি করা
এখন সব কাজ করছে, এটাকে এক্সপ্যান্ড করুন, হ্যাক করুন এবং আপনার বানান। ও হ্যা, শেয়ার করবেন না ভুলবেন! আমার কাছ থেকে শুনতে চাই। hello@jaredwolff.com
পোস্ট পছন্দ হলে নিচের শেয়ার লিংকগুলো দিয়ে বিশ্বের সাথে শেয়ার করুন। :)
এটি আমার ব্লগ থেকে ক্রস পোস্ট। আসলটি এখানে পাবেন।
আরো শিখতে আগ্রহী? Particle Platform-এর সর্বোচ্চ ফলাফল পাওয়ার গাইড লিখছি। এখানে আরো জানুন।