পাঠ ৪৬ · ৫৭-এর মধ্যে · মডিউল ১১
Home / Courses / Microprocessors, Embedded Systems & IoT / MQTT প্রোটোকল

MQTT প্রোটোকল

The MQTT protocol — publish/subscribe for IoT
১০ মিনিট পড়া মধ্যম · Intermediate Python সিমুলেশনসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • MQTT-এর পাবলিশ-সাবস্ক্রাইব আর্কিটেকচার — কেন এটি সরাসরি ডিভাইস-টু-ডিভাইস কমিউনিকেশনের চেয়ে ভালো ফিট IoT-এর জন্য
  • টপিক হায়ারার্কি এবং +/# ওয়াইল্ডকার্ড ম্যাচিংয়ের সঠিক নিয়ম
  • একটি জেনুইন ইন-মেমরি MQTT ব্রোকার সিমুলেশন তৈরি ও পরীক্ষা করা
  • বাস্তব-জগতের MQTT ব্রোকার (Mosquitto) ও এর সাধারণ ব্যবহার সম্পর্কে তথ্যভিত্তিক ধারণা

১ · কেন পাবলিশ-সাবস্ক্রাইব

MQTTMessage Queuing Telemetry Transportএকটি হালকা-ওজনের পাবলিশ-সাবস্ক্রাইব মেসেজিং প্রোটোকল, মূলত সীমিত ব্যান্ডউইথ ও অস্থির নেটওয়ার্কের জন্য ডিজাইন করা — IoT-তে সবচেয়ে বহুল ব্যবহৃত অ্যাপ্লিকেশন প্রোটোকল। হলো একটি হালকা-ওজনের মেসেজিং প্রোটোকল যা পাবলিশ-সাবস্ক্রাইবPublish/Subscribeপ্রেরক (পাবলিশার) ও প্রাপক (সাবস্ক্রাইবার) সরাসরি একে অপরকে চেনে না — উভয়ে শুধু একটি মধ্যস্থতাকারী ব্রোকারের সাথে কথা বলে। মডেল অনুসরণ করে। ধরুন একটি তাপমাত্রা সেন্সর সরাসরি একটি ড্যাশবোর্ড অ্যাপকে ডেটা পাঠাতে চাইলে, সেন্সরটিকে ড্যাশবোর্ডের ঠিকানা, সংযোগ-স্ট্যাটাস ইত্যাদি সবকিছু জানতে হতো — এবং নতুন কোনো অ্যাপ যোগ হলে সেন্সরের কোড বদলাতে হতো। MQTT-তে সেন্সরটি শুধু একটি ব্রোকারBrokerএকটি কেন্দ্রীয় সার্ভার যা সব পাবলিশার ও সাবস্ক্রাইবারের মধ্যে বার্তা রাউট করে — বাস্তব উদাহরণ: Mosquitto (ওপেন-সোর্স MQTT ব্রোকার)।-কে একটি টপিকTopicএকটি স্ট্রিং যা স্ল্যাশ (/) দিয়ে হায়ারার্কিক্যাল লেভেলে ভাগ করা — যেমন home/bed1/temperature। পাবলিশার একটি নির্দিষ্ট টপিকে বার্তা পাঠায়, সাবস্ক্রাইবাররা টপিক ফিল্টার দিয়ে সাবস্ক্রাইব করে।-এ বার্তা পাঠায় (পাবলিশ করে) — কে সেই বার্তা পাবে তা নিয়ে সেন্সরের কোনো ধারণাই থাকে না। যেকোনো সংখ্যক ক্লায়েন্ট সেই টপিকে সাবস্ক্রাইব করে বার্তা পেতে পারে, একেবারে নতুন কোড ছাড়াই। MQTT সাধারণত একটি একক, পার্সিস্টেন্ট TCP কানেকশনের ওপর চলে (ডিফল্ট পোর্ট ১৮৮৩, TLS-সহ ৮৮৮৩) — TCP/IP-এর সাধারণ মেকানিজম Computer Networks কোর্সে কভার করা হয়েছে, এই কোর্স সরাসরি IoT-স্পেসিফিক অ্যাপ্লিকেশন-লেয়ারে যাচ্ছে।

পাবলিশার (সেন্সর ডিভাইস) MQTT ব্রোকার (যেমন Mosquitto) সাবস্ক্রাইবার A টপিক ম্যাচ ✓ — ডেলিভার্ড সাবস্ক্রাইবার B টপিক মিসম্যাচ ✗ — ডেলিভারি নেই
ব্রোকার পাবলিশ হওয়া টপিকের সাথে প্রতিটি সাবস্ক্রাইবারের টপিক-ফিল্টার মিলিয়ে দেখে — শুধু জেনুইনভাবে ম্যাচ করা সাবস্ক্রাইবারদের কাছেই বার্তা যায়।
MQTT-কে একটি ম্যাগাজিন সাবস্ক্রিপশনের মতো ভাবুন — লেখক (পাবলিশার) জানেন না ঠিক কে কে পড়বে, তিনি শুধু একটি নির্দিষ্ট বিভাগে (টপিক) লেখা জমা দেন। যে পাঠক (সাবস্ক্রাইবার) সেই বিভাগে সাবস্ক্রাইব করে আছেন, তিনিই কপি পান — প্রকাশক (ব্রোকার) মাঝে থেকে পুরো বিতরণ ব্যবস্থাটি সামলায়।

২ · টপিক হায়ারার্কি ও ওয়াইল্ডকার্ড

একটি টপিক আসলে / দিয়ে ভাগ করা লেভেলের একটি ক্রম, যেমন home/bed1/temperature-এ তিনটি লেভেল: home, bed1, temperature। সাবস্ক্রাইব করার সময় একটি ক্লায়েন্ট একটি সুনির্দিষ্ট টপিকের বদলে একটি টপিক ফিল্টার দিতে পারে, যেখানে দুই ধরনের ওয়াইল্ডকার্ড ব্যবহার করা যায়:

  • + — সিঙ্গেল-লেভেল ওয়াইল্ডকার্ড: ঠিক একটি লেভেলের যেকোনো মান ম্যাচ করে। যেমন home/+/temperature, home/bed1/temperature-এর সাথে ম্যাচ করবে, কিন্তু home/bed1/sensor/temperature-এর সাথে করবে না (কারণ সেখানে অতিরিক্ত একটি লেভেল আছে)।
  • # — মাল্টি-লেভেল ওয়াইল্ডকার্ড: নিজের অবস্থান থেকে বাকি যত লেভেল থাকুক না কেন, সব ম্যাচ করে (এবং শুধু ফিল্টারের একদম শেষে বসতে পারে)। যেমন home/#, home/bed1/temperature এবং home/bed1/sensor/temperature — দুটোই ম্যাচ করবে।

৩ · একটি সত্যিকারের MQTT ব্রোকার সিমুলেশন

এই সাইটের স্যান্ডবক্সে বাস্তব নেটওয়ার্ক অ্যাক্সেস নেই, তাই নিচে একটি সম্পূর্ণ ইন-মেমরি MqttBroker ক্লাস লেখা হয়েছে যা subscribe() ও publish() মেথডসহ, ওয়াইল্ডকার্ড ম্যাচিং লজিক জেনুইনভাবে গণনা করে — প্রতিটি রাউটিং সিদ্ধান্ত সত্যিই কোডের ভেতর থেকে আসছে, কোথাও হার্ডকোড করা ফলাফল নেই।

Python
class MqttBroker:
    """একটি সরলীকৃত ইন-মেমরি MQTT ব্রোকার -- বাস্তব Mosquitto-এর মতো
    pub/sub রাউটিং লজিকের একটি জেনুইন সিমুলেশন।"""

    def __init__(self):
        self.subscriptions = []  # [(client_id, topic_filter), ...]

    def subscribe(self, client_id, topic_filter):
        self.subscriptions.append((client_id, topic_filter))

    @staticmethod
    def topic_matches(topic_filter, topic):
        # টপিক ও ফিল্টার উভয়কে '/' দিয়ে লেভেলে ভাগ করা হচ্ছে
        filter_levels = topic_filter.split("/")
        topic_levels = topic.split("/")

        i = 0
        while i < len(filter_levels):
            level = filter_levels[i]
            if level == "#":
                # '#' নিজের অবস্থান থেকে বাকি সবগুলো লেভেলের সাথে ম্যাচ করে
                return True
            if i >= len(topic_levels):
                # ফিল্টারে আরও লেভেল বাকি, কিন্তু টপিক আগেই শেষ হয়ে গেছে
                return False
            if level == "+" or level == topic_levels[i]:
                # '+' যেকোনো একটি লেভেলের সাথে ম্যাচ করে
                i += 1
                continue
            return False

        # ফিল্টার শেষ -- টপিকও ঠিক একই লেভেলে শেষ হতে হবে
        return i == len(topic_levels)

    def publish(self, topic, payload):
        recipients = []
        for client_id, topic_filter in self.subscriptions:
            if self.topic_matches(topic_filter, topic):
                recipients.append(client_id)
        return recipients


broker = MqttBroker()
broker.subscribe("dashboard-A", "home/+/temperature")
broker.subscribe("logger-B", "home/#")
broker.subscribe("livingroom-app", "home/livingroom/#")

print("সাবস্ক্রিপশন লিস্ট:")
for client_id, topic_filter in broker.subscriptions:
    print(f"  {client_id:15s} <- ফিল্টার: {topic_filter}")

test_topics = [
    "home/bed1/temperature",
    "home/bed1/sensor/temperature",
    "home/livingroom/temperature",
    "home/kitchen/humidity",
]

print("\nপ্রতিটি পাবলিশের রাউটিং সিদ্ধান্ত (broker.publish() থেকে সরাসরি গণনা করা):")
for topic in test_topics:
    recipients = broker.publish(topic, payload="...")
    shown = recipients if recipients else "(কোনো সাবস্ক্রাইবার ম্যাচ করেনি)"
    print(f"  PUBLISH '{topic}' -> ডেলিভার্ড: {shown}")

print("\nওয়াইল্ডকার্ড ম্যাচিং লজিক আলাদাভাবে যাচাই (topic_matches সরাসরি কল করে):")
checks = [
    ("home/+/temperature", "home/bed1/temperature"),          # + ঠিক এক লেভেল -> True
    ("home/+/temperature", "home/bed1/sensor/temperature"),   # + এক লেভেলের বেশি ম্যাচ করে না -> False
    ("home/#", "home/bed1/temperature"),                      # # বাকি সব লেভেল -> True
    ("home/#", "home/bed1/sensor/temperature"),                # # গভীরতর হলেও ম্যাচ করে -> True
    ("home/livingroom/#", "home/bed1/temperature"),            # ভিন্ন দ্বিতীয় লেভেল -> False
]
for topic_filter, topic in checks:
    result = MqttBroker.topic_matches(topic_filter, topic)
    print(f"  topic_matches('{topic_filter}', '{topic}') -> {result}")

    
লক্ষ্য করুন home/+/temperature সাবস্ক্রাইবার home/bed1/temperature পায়, কিন্তু home/bed1/sensor/temperature পায় না — কারণ + ঠিক একটি লেভেলের বেশি কভার করে না। অন্যদিকে home/# দুটোই পায়, কারণ # যেকোনো গভীরতার বাকি সব লেভেল কভার করে। এই পার্থক্যটাই বাস্তব MQTT সিস্টেমে সবচেয়ে বেশি ভুল বোঝাবুঝির জায়গা।

৪ · বাস্তব-জগতের ব্রোকার

বাস্তব IoT প্রজেক্টে এই ব্রোকারের কাজটি করে Mosquitto-এর মতো একটি প্রকৃত সফটওয়্যার (ওপেন-সোর্স, ব্যাপকভাবে ব্যবহৃত), অথবা ক্লাউড-হোস্টেড ব্রোকার সার্ভিস (M11-এর পরবর্তী পাঠ L50-এ এই বিষয়ে আরও আসবে)। ডিভাইসের ফার্মওয়্যার সাধারণত C/C++-এ লেখা একটি MQTT ক্লায়েন্ট লাইব্রেরি ব্যবহার করে ব্রোকারের সাথে সংযোগ করে — এই পাঠের Python কোডটি সেই আন্ডারলাইং প্রোটোকল-লজিকের একটি সিমুলেশন মাত্র, প্রকৃত ফার্মওয়্যার নয়।

মূল কথা · Key takeaway

MQTT-এর কেন্দ্রে আছে একটি ব্রোকার, একটি স্ল্যাশ-বিভক্ত টপিক হায়ারার্কি, এবং দুই ধরনের ওয়াইল্ডকার্ড (+ = এক লেভেল, # = বাকি সব লেভেল) — এই ম্যাচিং লজিকই ঠিক করে দেয় কোন বার্তা কোন সাবস্ক্রাইবারের কাছে পৌঁছাবে। L47-এ আমরা এই একই ব্রোকারকে আরও বাস্তবসম্মত করব — QoS ডেলিভারি গ্যারান্টি, রিটেইনড মেসেজ, ও লাস্ট-উইল যোগ করে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ উপরের কোডে যদি logger-B ফিল্টার home/#-এর বদলে শুধু # হতো, তাহলে topic_matches("#", "home/kitchen/humidity") ফলাফল কী হতো এবং কেন?

এখনও True হতো। কারণ লুপের প্রথম ধাপেই (i=0) filter_levels[0] সরাসরি "#", তাই ফাংশনটি অবিলম্বে True রিটার্ন করে — টপিকের প্রথম লেভেল কী তা যাচাই না করেই। একা # মানে "সব টপিক" — বাস্তব MQTT ব্রোকারেও এটি বৈধ, যদিও প্রায় সব বার্তা ম্যাচ করে ফেলে বলে সাধারণত প্রোডাকশনে সতর্কতার সাথে ব্যবহার করা হয়।

প্র ০২ topic_matches ফাংশনে i >= len(topic_levels) চেকটি ঠিক কোন পরিস্থিতি ধরার জন্য আছে?

এটি সেই পরিস্থিতি ধরে যেখানে ফিল্টারে এখনও লেভেল বাকি আছে (যেমন + বা একটি সুনির্দিষ্ট নাম), কিন্তু টপিক স্ট্রিং আগেই শেষ হয়ে গেছে — যেমন ফিল্টার home/+/temperature বনাম টপিক home/bed1 (মাত্র দুই লেভেল)। এই চেক ছাড়া কোডটি topic_levels[i]-এ ইনডেক্স এরর দিত; এই চেকটি সেটিকে একটি জেনুইন "no match" (False) হিসেবে হ্যান্ডল করে।

প্র ০৩ বাস্তব জীবনে কেন MQTT-তে ডিভাইস সরাসরি একে অপরকে না পাঠিয়ে সবসময় ব্রোকারের মাধ্যমে বার্তা পাঠায়?

কারণ ব্রোকার-ভিত্তিক ডিজাইনে পাবলিশার ও সাবস্ক্রাইবার একে অপরের ঠিকানা, অনলাইন-অফলাইন স্ট্যাটাস, বা সংখ্যা জানার দরকার নেই — এই ডিকাপলিং-এর কারণেই নতুন সাবস্ক্রাইবার (যেমন একটি নতুন মোবাইল অ্যাপ) যোগ করতে বিদ্যমান সেন্সর ডিভাইসের কোনো পরিবর্তন লাগে না। একটি সীমিত-রিসোর্সের মাইক্রোকন্ট্রোলারের জন্য এটি বিশেষভাবে গুরুত্বপূর্ণ — ডিভাইসটিকে শুধু একটি সংযোগ (ব্রোকারের সাথে) ও একটি সাধারণ প্রোটোকল বজায় রাখলেই চলে।

অনুশীলন

  1. চিন্তা করুন: ধরুন একজন নতুন সাবস্ক্রাইবার "security-cam" ফিল্টার +/+/motion দিয়ে সাবস্ক্রাইব করেছে। এটি কি home/garage/motion টপিকের সাথে ম্যাচ করবে বলে আপনার ধারণা?

    হ্যাঁ, ম্যাচ করবে। ফিল্টারের লেভেল ["+", "+", "motion"] আর টপিকের লেভেল ["home", "garage", "motion"] — প্রথম + ম্যাচ করে home-এর সাথে, দ্বিতীয় + ম্যাচ করে garage-এর সাথে, আর শেষ লেভেল motion সরাসরি মিলে যায়। একাধিক + একই ফিল্টারে একসাথে ব্যবহার করা সম্পূর্ণ বৈধ।

  2. পরীক্ষা করুন: উপরের কোড সেলে test_topics লিস্টে "home/livingroom/light/status" যোগ করুন এবং Run চেপে দেখুন কোন কোন সাবস্ক্রাইবার এটি পায়।

    dashboard-A (home/+/temperature) পাবে না, কারণ শেষ লেভেল temperature নয়। logger-B (home/#) পাবে, কারণ # যেকোনো গভীরতার সব বাকি লেভেল কভার করে। livingroom-app (home/livingroom/#) পাবে, কারণ দ্বিতীয় লেভেল livingroom-এ মিলে গেছে এবং তারপরের # বাকি light/status কভার করে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

  • কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ মাইক্রোপ্রসেসর আর্কিটেকচার, এমবেডেড C, GPIO, টাইমার/PWM/ADC, সিরিয়াল প্রোটোকল, RTOS, সেন্সর/অ্যাকচুয়েটর, IoT আর্কিটেকচার, ওয়্যারলেস প্রোটোকল, MQTT/CoAP ও IoT সিকিউরিটি — বাকি পাঠগুলো শীঘ্রই যুক্ত হবে।
  • Computer Networks কোর্স সহোদর কোর্স TCP/IP স্ট্যাক ও সাধারণ নেটওয়ার্কিং সেই কোর্সেই কভার করা হয়েছে — এই কোর্স সরাসরি IoT-স্পেসিফিক অ্যাপ্লিকেশন প্রোটোকলে যায়।
  • সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Design and Analysis of Algorithms ও আরও অনেক কোর্স — সব এক জায়গায়।
আগের পাঠ
সঠিক IoT কানেক্টিভিটি বেছে নেওয়া