در دنیای امروز، اینترنت اشیا (IoT) به یکی از مهمترین شاخههای فناوری تبدیل شده که در آن دستگاهها و سنسورها از طریق شبکه با یکدیگر در ارتباط هستند و دادهها را جمعآوری، پردازش و منتقل میکنند. در چنین پروژههایی، استفاده از ماژولهای 4G مانند Quectel EC200U به عنوان پل ارتباطی بین سختافزار و اینترنت، نقش حیاتی دارد.
در این پست قصد دارم روش راهاندازی این ماژول با استفاده از SDK رسمی EC200U به زبان C را با شما به اشتراک بگذارم. تمرکز اصلی من بر روی پروتکلهای مهم و قابلیت های کلیدی است که تقریباً در تمام پروژههای IoT کاربرد دارند:
✅ CSQ , GSM: بررسی کیفیت سیگنال و وضعیت سیمکارت برای اطمینان از پایداری ارتباط، ارسال پیامک و تماس.
✅ MQTT: پروتکلی سبک و سریع برای ارسال دادههای لحظهای بین دستگاهها و سرورهای ابری در پروژههای IoT.
✅ HTTP: پروتکلی ساده و فراگیر برای ارسال و دریافت داده از طریق وبسرورها، مثل ثبت داده یا دریافت تنظیمات.
✅ Ping: تست اتصال شبکه برای بررسی دسترسی، کیفیت لینک و تاخیر ارتباطی.
✅ NTP: هماهنگسازی ساعت دستگاهها با سرورهای زمانی دقیق برای ثبت درست رویدادها و امنیت ارتباطات.
✅ Socket: ارتباط دوطرفه مستقیم بین کلاینت و سرور برای انتقال سریع دادهها بهصورت پایدار.
✅ OTA: آپدیت نرمافزار دستگاه از راه دور برای رفع باگ، افزودن قابلیتها یا افزایش امنیت.
استفاده از این قابلیتها در برنامههای C با SDK ماژول، نیازمند درک دقیق از ساختار توابع، مدیریت ارتباطات سریال، نحوهی کار با APIهای موجود، و در برخی موارد، تحلیل پاسخ Callbackهای مربوطه است. هدف من از این پست، ارائهی یک دید عملی برای توسعهدهندگانی است که میخواهند بدون وابستگی به فریمورکهای سطح بالا، بهصورت مستقیم و سطح پایین با سختافزار کار کنند. اگر شما هم به دنبال پیادهسازی پروتکلهای ارتباطی با زبان C با یک ماژول 4G هستید ادامهی این آموزش میتواند راه را برایتان هموارتر کند.
لازم به ذکر است که به دلیل زیاد بودن تعداد پروتکلها سعی شده آموزش به طور کابردی باشه. همچنین ارتباطی که با ماژول برقرار میکنیم ازطریق UART است و کد مشابه آن چیزی است که در قسمت قبل بیان شد و در اینجا تنها نام تابع UART عوض میشود.
برای بررسی وضعیت سیگنال و سیمکارت نیاز است که ابتدا از این مسیر (/components/ql-application/init/ql_init.c) تابع (ql_sim_app_init) را فعال کنید. توابع مهمی که ما در این بخش استفاده میکنیم توابع زیر هستند که بعدا نیز از آنها برای بررسی وضعیت سیمکارت بهره میبریم. این توابع همانطور که مشخص است داده های (IMSI,ICCID,phonenumber) را از سیمکارت دریافت میکنند.
1 2 3 4 5 6 7 8 | ret = ql_sim_get_imsi(sim_id, siminfo, 64); QL_SIM_LOG("ret:0x%x, IMSI: %s", ret, siminfo); ret = ql_sim_get_iccid(sim_id, siminfo, 64); QL_SIM_LOG("ret:0x%x, ICCID: %s", ret, siminfo); ret = ql_sim_get_phonenumber(sim_id, siminfo, 64); QL_SIM_LOG("ret:0x%x, phonenumber: %s", ret, siminfo); |
دقت کنید که اگر توابع مربوط به PIN سیمکارت را لازم ندارید آنها را غیرفعال کنید. پس از اینکه از وضعیت سیمکارت اطمینان پیداکردیم سراغ برقراری تماس و ارسال پیامک میرویم.
همچنین، تماس صوتی میتواند برای ارسال پیامهای صوتی اضطراری یا تأیید هویت انسانی در سیستمهای امنیتی و نظارتی مورد استفاده قرار گیرد. استفاده از این قابلیتها در پروژههای IoT، نه تنها باعث افزایش پایداری ارتباط در شرایط بحرانی میشود، بلکه امکان گسترش کاربردها به مناطق دورافتاده و کمبرخوردار از زیرساخت اینترنت را نیز فراهم میکند. از اینرو، ترکیب تماس و پیامک با فناوریهای جدید، موجب افزایش انعطافپذیری، امنیت و پوشش در طراحی سیستمهای هوشمند میگردد. برای تماس صوتی لازم است ابتدا کد زیر را در مسیر (/components/ql-application/init/ql_init.c) از حالت کامنت خارج کنید.
1 2 3 | #ifdef QL_APP_FEATURE_VOICE_CALL ql_voice_call_app_init(); #endif |
ساختار کد برنامه تماس که در فایل voice_call_demo.c قراردارد کاملا مانند چیزیست که در آموزشهای قبل دیدین به همین دلیل به جای بررسی ساختار کد به سراغ بررسی خود کد میرویم. در این کد مثالهایی برای شما قرارداده شده تا بتوانید از قابلیتهای (Disable automatic answer,Enable call waiting,call forwarding,call clccc) استفاده کنید. به دلیل واضح بودن کد و طولانی نشدن مطلب از تفسیر و بررسی کامل تمام قابلیتها صرف نظر میکنیم و تنها مهمترین قسمت یعنی برقراری تماس را بررسی میکنیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // #if 0 //demo for ql_voice_call_clcc ql_voice_call_start(nSim,"0912xxxxxx"); ql_rtos_task_sleep_s(15); uint8_t total; ql_vc_info_s vc_info[QL_VC_MAX_NUM]; err = ql_voice_call_clcc(nSim,&total, vc_info); if(err != QL_VC_SUCCESS){ QL_VC_LOG("ql_voice_call_clcc FAIL"); }else { QL_VC_LOG("total number = %d",total); for(uint8_t i = 0; i<total; i++){ QL_VC_LOG("index:%d direction:%d status:%d mpty:%d number:%s", vc_info[i].idx,vc_info[i].direction,vc_info[i].status,vc_info[i].multiparty,vc_info[i].number); } } // #endif |
با خارج کردن این قسمت کد از حالت کامنت و قراردادن شماره خود بجای “0912xxxxxx” میتوانید تماس برقرارکنید. چنانچه تماس با موفقیت برقرار شد به سراغ ارسال پیامک بروید.
برای ارسال پیامک نیاز است که ابتدا از این مسیر (/components/ql-application/init/ql_init.c) تابع (ql_sms_app_init) را از حالت کامنت خارج کنید.
1 2 3 | #ifdef QL_APP_FEATURE_SMS // ql_sms_app_init(); #endif |
سپس نیاز است در توابع مربوط به پیامک این قسمت را از حالت کامنت خارج کنید.
1 2 3 4 5 6 7 8 | // #if 0 //Send English text message if(QL_SMS_SUCCESS == ql_sms_send_msg(nSim,"0912xxxxxx","~!@#$%^&*()_+<>?:{}|", GSM)){ QL_SMS_LOG("ql_sms_send_msg OK"); }else{ QL_SMS_LOG("ql_sms_send_msg FAIL"); } |
با خارج کردن این قسمت کد از حالت کامنت و قراردادن شماره خود بجای “0912xxxxxx” میتوانید پیامک ارسال کنید. اگر با تماس و پیامک مشکلی نداشتید حالا وقتش است که به شبکه جهانی متصل شویم. برای تست اتصال یکی از بهترین ابزارها Ping است که در ادامه توضیح داده میشود.
این ابزار بهقدری مهم است که در بسیاری از شبکهها، اولین گام در فرآیند عیبیابی محسوب میشود و متخصصان شبکه با کمک نتایج آن میتوانند تصمیم بگیرند که مشکل از سمت کلاینت، سرور، یا مسیر میان آنهاست. بهطور خلاصه، Ping ابزاری کمحجم، سریع و در دسترس در تمامی سیستمعاملهاست که نقش کلیدی در مانیتورینگ لحظهای، تست دسترسی و تحلیل ابتدایی کیفیت ارتباط شبکه ایفا میکند و همین قابلیتهای ساده اما کاربردی، باعث شده تا یکی از پایهایترین ابزارها در جعبهابزار هر مدیر شبکه و تکنسین IT باشد.
برای استفاده از ping نیاز است که ابتدا از این مسیر (/components/ql-application/init/ql_init.c) تابع (ql_ping_app_init) را از حالت کامنت خارج کنید.
1 2 3 | #ifdef QL_APP_FEATURE_PING //ql_ping_app_init(); #endif |
تابعی که برای درخواست پینگ استفاده میکنیم.
1 | ping_sess_id = ql_ping_start(profile_idx, 0, "www.google.com", &ping_option, ping_event_cb); |
خروجی مانند زیر خواهید داشت:
چنانچه خروجی مانند بالا گرفتید یعنی ارتباط شما با شبکه برقرار است و میتوانیم سراغ سایر پروتکلها برویم. اولین پروتکلی که بررسی خواهیم کرد پروتکل پر استفاده ی NTP است.
پروتکل NTP با معماری سلسلهمراتبی (Stratum) کار میکند و این امکان را فراهم میآورد که دستگاهها، ساعت خود را از سرورهای زمانی دقیق (مانند ساعت اتمی یا GPS) دریافت کرده و با دقتی در حد میلیثانیه هماهنگ شوند. این پروتکل علاوه بر انتقال زمان، مکانیزمهایی برای جبران تاخیر شبکه و کاهش انحراف زمانی (Drift) ارائه میدهد. مزایا و دلایل استفاده از NTP به این شرح است.
یکسانسازی زمان در شبکههای توزیعشده: هماهنگی ساعت سیستمها باعث میشود رویدادها بهدرستی ثبت شوند و تحلیل لاگها قابل اعتماد باشد.
برای استفاده از NTP نیاز است که ابتدا از این مسیر (/components/ql-application/init/ql_init.c) تابع (ql_ntp_app_init) را از حالت کامنت خارج کنید.
1 2 3 | #ifdef QL_APP_FEATURE_NTP ql_ntp_app_init(); #endif |
برای دیدن لاگ NTP روی UART از کد زیر استفاده کردم و تابع لاگ اصلی این برنامه یعنی تابع (QL_NTP_LOG) را در کد با تابعی که ساختیم جایگزین کردم. برای اینکار میتوانید از کلید میانبر Ctrl+H در Vscode استفاده کنید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #include "ql_pin_cfg.h" #include "ql_uart.h" #define UART_LOG_BUFF_SIZE 512 // Buffer size for formatted output void ntp_uart_log(const char *format, ...) { char buffer[UART_LOG_BUFF_SIZE]; // Local buffer va_list args; va_start(args, format); vsnprintf(buffer, sizeof(buffer) - 3, format, args); // Reserve space for "\r\n" va_end(args); strcat(buffer, "\r\n"); // Automatically add "\r\n" int ret = 0; ql_uart_config_s uart_cfg = {0}; uart_cfg.baudrate = QL_UART_BAUD_115200; uart_cfg.flow_ctrl = QL_FC_NONE; uart_cfg.data_bit = QL_UART_DATABIT_8; uart_cfg.stop_bit = QL_UART_STOP_1; uart_cfg.parity_bit = QL_UART_PARITY_NONE; ret = ql_uart_set_dcbconfig(QL_UART_PORT_1, &uart_cfg); ret = ql_pin_set_func(QL_UART2_TX_PIN, QL_UART2_TX_FUNC); ret = ql_pin_set_func(QL_UART2_RX_PIN, QL_UART2_RX_FUNC); ret = ql_uart_open(QL_UART_PORT_1); ret = ql_pin_set_func(QL_UART2_TX_PIN, QL_UART2_TX_FUNC); if (QL_UART_SUCCESS == ret) { ql_uart_get_dcbconfig(QL_UART_PORT_1, &uart_cfg); ql_uart_write(QL_UART_PORT_1, (unsigned char*)buffer, strlen(buffer)); } } |
قسمت دیگر کد که نیاز به تغییر دارد به صورت زیر است. با ایجاد این تغییر از یک سرور NTP ایرانی استفاده میکنیم.
1 | ntp_cli_id = ql_ntp_sync("2.ir.pool.ntp.org", &sync_option, ntp_result_cb, NULL, &error_num); |
پس از تغییر کد و کامپایل برنامه خروجی مثل زیر خواهیم داشت.
چنانچه برنامه به درستی کار کرد در قسمت بعدی با قابلیت شگف انگیز بروزرسانی از راه دور آشنا خواهید شد.
FOTA به سازندگان و مدیران شبکه این امکان را میدهد تا میکروکنترلرها، مودمها یا ماژولهای ارتباطی را در مقیاس بزرگ، سریع و ایمن بهروزرسانی کنند. این موضوع بهویژه برای رفع آسیبپذیریهای امنیتی و افزایش طول عمر محصولات متصل اهمیت زیادی دارد. در مجموع، OTA و FOTA باعث کاهش هزینههای نگهداری، افزایش رضایت کاربران و مدیریت آسان دستگاههای نصبشده در نقاط مختلف میشوند.
برای استفاده از OTA نیاز است که ابتدا از این مسیر (/components/ql-application/init/ql_init.c) تابع (ql_fota_http_app_init) را فعال کنید.
1 2 3 | #ifdef QL_APP_FEATURE_HTTP_FOTA //ql_fota_http_app_init(); #endif |
قبل از اینکه سراغ کد برویم لازم است کد قبلی و کد جدید را به این دایرکتوری ببرید (SDK_U/tools/win32) و از ابزاری به نام dtools استفاده کنید که برای شما یک فایل جهت آپدیت کردن میسازد. این فایل بسته به دستوری که به dtools میدهید میتواند از به طور کامل باشد و یا با استفاده از دستور زیر فقط شامل تغییرات مربوط به دو نسخه باشد. یعنی این ابزار هر دو برنامه را بررسی میکند و فقط تغییراتی را که ایجاد کرده اید به صورت یک فایل در اختیار شما میگذارد که اگر روی یک سرور قراردهید میتوانید از آن استفاده کنید. در آزمایشی که انجام شد تابع OTA حذف نشد و بعد از اینکه سیستم به طور خودکار راه اندازی مجدد شد سیستم به درستی کار کرد. برای استفاده از این برنامه لازم است دستور زیر را در دایرکتوری (SDK_U/tools/win32) بزنید (اگر از ویندوز استفاده میکنید نیاز به کلمه wine ندارید و این کلمه را حذف کنید.)
1 | wine dtools.exe fotacreate2 --pac aa.pac,bb.pac,setting/fota8910.xml output.pack -d v |
در اینجا اسم برنامه جدید bb است و برنامه ای که برای ما میسازد (output.pack) نام دارد که باید در سرور قراردهیم. درصورتی که اینکار به طور صحیح انجام شود خروجی به شکل زیر خواهد بود
حالا در کد نیاز است که این قسمت ها را تغییر دهیم. ابتدا آدرس سرور را در این قسمت کد وارد میکنیم.
1 | #define HTTP_DLOAD_URL "http://188.136.xxx.xxx:10xx/output.pack" |
در قسمت دیگری از کد هم اسم فایل را وارد میکنیم.
1 | memcpy(fota_http_cli_p->fota_packname,"UFS:output.pack",strlen("UFS:output.pack")); |
پس از نصب برنامه روی دستگاه خروجی مانند زیر خواهیم داشت.
چنانچه پیام موفقیت را ببینید دستگاه شما به درستی بروزرسانی شده. در ادامه پروتکل پر استفاده و کارآمد MQTT را بررسی خواهیم کرد.
برای ارسال داده با MQTT نیاز است که ابتدا از این مسیر (/components/ql-application/init/ql_init.c) تابع (ql_mqtt_app_init) را فعال کنید.
برای دریافت داده های (log) برنامه از این کد استفاده میکنیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | #include "ql_pin_cfg.h" #include "ql_uart.h" #define UART_LOG_BUFF_SIZE 512 // Buffer size for formatted output void mqtt_ql_uart_notify_cb_main(unsigned int ind_type, ql_uart_port_number_e port, unsigned int size) { unsigned char *recv_buff = calloc(1, QL_UART_RX_BUFF_SIZE+1); unsigned int real_size = 0; int read_len = 0; switch(ind_type) { case QUEC_UART_RX_OVERFLOW_IND: //rx buffer overflow case QUEC_UART_RX_RECV_DATA_IND: { while(size > 0) { memset(recv_buff, 0, QL_UART_RX_BUFF_SIZE+1); real_size= MIN(size, QL_UART_RX_BUFF_SIZE); read_len = ql_uart_read(port, recv_buff, real_size); if((read_len > 0) && (size >= read_len)) { size -= read_len; } else { break; } } break; } case QUEC_UART_TX_FIFO_COMPLETE_IND: { break; } } free(recv_buff); recv_buff = NULL; } void mqtt_uart_log(const char *format, ...) { char buffer[UART_LOG_BUFF_SIZE]; // Local buffer va_list args; va_start(args, format); vsnprintf(buffer, sizeof(buffer) - 3, format, args); // Reserve space for "\r\n" va_end(args); strcat(buffer, "\r\n"); // Automatically add "\r\n" int ret = 0; ql_uart_config_s uart_cfg = {0}; uart_cfg.baudrate = QL_UART_BAUD_115200; uart_cfg.flow_ctrl = QL_FC_NONE; uart_cfg.data_bit = QL_UART_DATABIT_8; uart_cfg.stop_bit = QL_UART_STOP_1; uart_cfg.parity_bit = QL_UART_PARITY_NONE; ret = ql_uart_set_dcbconfig(QL_UART_PORT_1, &uart_cfg); ret = ql_pin_set_func(QL_UART2_TX_PIN, QL_UART2_TX_FUNC); ret = ql_pin_set_func(QL_UART2_RX_PIN, QL_UART2_RX_FUNC); ret = ql_uart_open(QL_UART_PORT_1); ret = ql_pin_set_func(QL_UART2_TX_PIN, QL_UART2_TX_FUNC); if (QL_UART_SUCCESS == ret) { ret = ql_uart_register_cb(QL_UART_PORT_1, mqtt_ql_uart_notify_cb_main); ql_uart_get_dcbconfig(QL_UART_PORT_1, &uart_cfg); ql_uart_write(QL_UART_PORT_1, (unsigned char*)buffer, strlen(buffer)); } } |
در اینجا تابعی که مربوط به دریافت داده از UART هست را هم در کد قرار دادم. شما میتوانید ایجاد کمی تغییرات در کد از آن برای ارسال پیام دلخواه به سرور استفاده کنید. در اینجا پیامی که میفرستیم به طور ثابت (temp:5) است و از سرور رایگان Thingsboard استفاده کردیم. برای استفاده از این سرور نیاز است ابتدا تنظیمات سرور را به صورت زیر وارد کنید.
1 2 3 4 5 6 7 8 9 10 | #define MQTT_CLIENT_IDENTITY "a6f0d900-3477-11f0-8a95-4f20f9529836"//ID #define MQTT_CLIENT_USER "1HAuMBQu8bI8gaCwymGB" //Token #define MQTT_CLIENT_PASS "" #define MQTT_CLIENT_QUECTEL_URL "mqtt://mqtt.eu.thingsboard.cloud:1883" #define MQTT_PUB_MSG0 "{\"temp\":5}" const char pu_topic[] = "v1/devices/me/telemetry\0"; //Publisher topic const char su_topic[] = "v1/devices/me/attributes\0"; //Subscriber topic |
کد ما دو تابع کلی دارد که یکی توابع بازگشتی (callback) هستند که به صورت زیر است.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | //define process state typedef enum { STATE_NET_CONN, //Connect to NET STATE_MQTT_CFG, //set MQTT parameters STATE_MQTT_OPEN, //open mqtt STATE_MQTT_CONN, //connect to server STATE_MQTT_SUB, //subscribe to server STATE_MQTT_PUB, //publish to server STATE_MQTT_SEND_DATA, STATE_MQTT_CLOSE, //close connection STATE_MQTT_DISC, //disconnect from server STATE_MQTT_DONE //done } MQTT_STATE; MQTT_STATE mqtt_state = STATE_NET_CONN; static ql_sem_t mqtt_semp; static int mqtt_connected = 0; static void mqtt_state_exception_cb(mqtt_client_t *client) { mqtt_uart_log("mqtt session abnormal disconnect\r\n"); mqtt_connected = 0; } //Mqtt connect static void mqtt_connect_result_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_e status) { mqtt_uart_log("status: %d\r\n", status); if(status == 0){ mqtt_connected = 1; mqtt_uart_log("MQTT is Connected\r\n"); } ql_rtos_semaphore_release(mqtt_semp); } static void mqtt_disconnect_result_cb(mqtt_client_t *client, void *arg,int err){ mqtt_uart_log("err: %d\r\n", err); mqtt_uart_log("mqtt disconnect\r\n"); ql_rtos_semaphore_release(mqtt_semp); } static void mqtt_requst_result_cb(mqtt_client_t *client, void *arg,int err) { mqtt_uart_log("err: %d\r\n", err); mqtt_uart_log("mqtt_requst_result\r\n"); ql_rtos_semaphore_release(mqtt_semp); } static void mqtt_inpub_data_cb(mqtt_client_t *client, void *arg, int pkt_id, const char *topic, const unsigned char *payload, unsigned short payload_len) { mqtt_uart_log("topic: %s\r\n", topic); mqtt_uart_log("payload: %s\r\n", payload); mqtt_uart_log("mqtt_inpub_data\r\n"); } |
و تابع دیگری که کار اصلی را انجام میدهد تابع (mqtt_app_thread) است که در ادامه مشاهده میکنید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | static void mqtt_app_thread(void * arg) { int ret = 0,i = 0, run_num = 1,profile_idx = 1; uint8_t nSim = 0; uint16_t sim_cid=0; char ip4_addr_str[16] = {0}; ql_data_call_info_s info; QlOSStatus OsErrorMsg=0; mqtt_client_t mqtt_cli; struct mqtt_connect_client_info_t client_info = {0}; client_info.keep_alive = 60; client_info.clean_session = 1; client_info.will_qos = 0; client_info.will_retain = 0; client_info.will_topic = NULL; client_info.will_msg = NULL; client_info.client_id = MQTT_CLIENT_IDENTITY; client_info.client_user = MQTT_CLIENT_USER; client_info.client_pass = MQTT_CLIENT_PASS; char siminfo[64] = {0}; uint8_t sim_id = 0; ret = ql_sim_get_imsi(sim_id, siminfo, 64); mqtt_uart_log("IMSI:%s\r\n", siminfo); ret = ql_sim_get_iccid(sim_id, siminfo, 64); mqtt_uart_log("ICCID:%s\r\n", siminfo); ql_rtos_task_sleep_s(1); OsErrorMsg=ql_rtos_semaphore_create(&mqtt_semp, 0); mqtt_uart_log("rtos_semaphore_create%d\r\n", OsErrorMsg); mqtt_uart_log("========== mqtt demo start ==========\r\n"); while (1) { switch (mqtt_state) { case STATE_NET_CONN: while((ret = ql_network_register_wait(nSim, 120)) != 0 && i < 10) { i++; ql_rtos_task_sleep_s(1); } if(ret == 0) { i = 0; mqtt_uart_log("====network registered!!!!====\r\n"); } else { mqtt_uart_log("====network register failure!!!!!====\r\n"); goto exit; } ql_set_data_call_asyn_mode(nSim, profile_idx, 0); mqtt_uart_log("===start data call====\r\n"); ret=ql_start_data_call(nSim, profile_idx, QL_PDP_TYPE_IP, "uninet", NULL, NULL, 0); mqtt_uart_log("data call result:%d\r\n", ret); //ret=0 if(ret != 0) { mqtt_uart_log("====data call failure!!!!=====\r\n"); } else{ mqtt_uart_log("-*data success*-\r\n"); } memset(&info, 0x00, sizeof(ql_data_call_info_s)); ret = ql_get_data_call_info(nSim, profile_idx, &info); if(ret != 0) { mqtt_uart_log("ql_get_data_call_info error. ret: %d\r\n", ret); ql_stop_data_call(nSim, profile_idx); goto exit; } else { mqtt_uart_log("ql_get_data_call_info success\r\n"); mqtt_uart_log("info->profile_idx: %d\r\n", info.profile_idx); //1 mqtt_uart_log("info->ip_version: %d\r\n", info.ip_version); //1 mqtt_uart_log("info->v4.state: %d\r\n", info.v4.state); //1 ql_rtos_task_sleep_s(2); inet_ntop(AF_INET, &info.v4.addr.ip, ip4_addr_str, sizeof(ip4_addr_str)); //ip4addr_ntoa_r((const ip4_addr_t*)(src),(dst),(size)) : NULL)) mqtt_uart_log("info.v4.addr.ip: %s\r\n", ip4_addr_str); inet_ntop(AF_INET, &info.v4.addr.pri_dns, ip4_addr_str, sizeof(ip4_addr_str)); mqtt_uart_log("info.v4.addr.pri_dns: %s\r\n", ip4_addr_str); inet_ntop(AF_INET, &info.v4.addr.sec_dns, ip4_addr_str, sizeof(ip4_addr_str)); mqtt_uart_log("info.v4.addr.sec_dns: %s\r\n", ip4_addr_str); } if(QL_DATACALL_SUCCESS != ql_bind_sim_and_profile(nSim, profile_idx, &sim_cid)) { mqtt_uart_log("nSim or profile_idx is invalid!!!!\r\n"); } //End Connect to NET mqtt_state=STATE_MQTT_CFG; // break; case STATE_MQTT_CFG: //init Mqtt client if(ql_mqtt_client_init(&mqtt_cli, sim_cid) != MQTTCLIENT_SUCCESS) { mqtt_uart_log("mqtt client init failed!!!!\r\n"); } else { mqtt_uart_log("mqtt client init OK\r\n"); //ok } mqtt_state=STATE_MQTT_CONN; break; case STATE_MQTT_CONN: //Complete the configuration of the mqtt client context, determine the mqtt connection properties, and establish the mqtt connect connection ret = ql_mqtt_connect(&mqtt_cli, MQTT_CLIENT_QUECTEL_URL , mqtt_connect_result_cb, NULL, (const struct mqtt_connect_client_info_t *)&client_info, mqtt_state_exception_cb); mqtt_uart_log("MQTT URL:%s, Client ID:%s, Client Session:%d, Client Pass:%s, Client User:%s\r\n",MQTT_CLIENT_QUECTEL_URL, client_info.client_id,client_info.clean_session,client_info.client_pass,client_info.client_user); mqtt_uart_log("====MQTTCLIENT_WOUNDBLOCK====\r\n"); if(ret == MQTTCLIENT_WOUNDBLOCK){ mqtt_uart_log("wait connect result\r\n"); ql_rtos_semaphore_wait(mqtt_semp, 10000); //QL_WAIT_FOREVER if(mqtt_connected == 0){ ql_mqtt_client_deinit(&mqtt_cli); mqtt_uart_log("ql_mqtt_client_deinit\r\n"); } } switch (ret) { case MQTTCLIENT_SUCCESS: mqtt_uart_log("MQTTCLIENT_SUCCESS\r\n"); break; case MQTTCLIENT_INVALID_PARAM: mqtt_uart_log("MQTTCLIENT_INVALID_PARAM\r\n"); break; case MQTTCLIENT_WOUNDBLOCK: mqtt_uart_log("====wait connect result\r\n"); ql_rtos_semaphore_wait(mqtt_semp, 1000); //QL_WAIT_FOREVER if(mqtt_connected == 0){ ql_mqtt_client_deinit(&mqtt_cli); mqtt_uart_log("MQTTCLIENT_WOUNDBLOCK\r\n"); } break; case MQTTCLIENT_OUT_OF_MEM: mqtt_uart_log("MQTTCLIENT_OUT_OF_MEM\r\n"); break; case MQTTCLIENT_ALLOC_FAIL: mqtt_uart_log("MQTTCLIENT_ALLOC_FAIL\r\n"); break; case MQTTCLIENT_TCP_CONNECT_FAIL: mqtt_uart_log("MQTTCLIENT_TCP_CONNECT_FAIL\r\n"); break; case MQTTCLIENT_NOT_CONNECT: mqtt_uart_log("MQTTCLIENT_NOT_CONNECT\r\n"); break; case MQTTCLIENT_SEND_PKT_FAIL: mqtt_uart_log("MQTTCLIENT_SEND_PKT_FAIL\r\n"); break; case MQTTCLIENT_BAD_REQUEST: mqtt_uart_log("MQTTCLIENT_BAD_REQUEST\r\n"); break; case MQTTCLIENT_TIMEOUT: mqtt_uart_log("MQTTCLIENT_TIMEOUT\r\n"); break; default: mqtt_uart_log("UNKNOWN_ERROR_CODE\r\n"); break; } mqtt_state=STATE_MQTT_SUB; break; case STATE_MQTT_SUB: ql_mqtt_set_inpub_callback(&mqtt_cli, mqtt_inpub_data_cb, NULL); mqtt_uart_log("ql_mqtt_set_inpub_callback\r\n"); if(ql_mqtt_sub_unsub(&mqtt_cli, "test", 1, mqtt_requst_result_cb,NULL, 1) == MQTTCLIENT_WOUNDBLOCK){ mqtt_uart_log("======wait subscrible result\r\n"); ql_rtos_semaphore_wait(mqtt_semp, 1000); } if(ql_mqtt_publish(&mqtt_cli, pu_topic,MQTT_PUB_MSG0, strlen(MQTT_PUB_MSG0), 0, 0, mqtt_requst_result_cb,NULL) == MQTTCLIENT_WOUNDBLOCK){ mqtt_uart_log("======wait publish result\r\n"); ql_rtos_semaphore_wait(mqtt_semp, QL_WAIT_FOREVER); } if(ql_mqtt_publish(&mqtt_cli, "test", "hi, mqtt qos 1", strlen("hi, mqtt qos 1"), 1, 0, mqtt_requst_result_cb,NULL) == MQTTCLIENT_WOUNDBLOCK){ mqtt_uart_log("======wait publish result\r\n"); ql_rtos_semaphore_wait(mqtt_semp, 1000); } if(ql_mqtt_publish(&mqtt_cli, "test", "hi, mqtt qos 2", strlen("hi, mqtt qos 2"), 2, 0, mqtt_requst_result_cb,NULL) == MQTTCLIENT_WOUNDBLOCK){ mqtt_uart_log("======wait publish result\r\n"); ql_rtos_semaphore_wait(mqtt_semp, 1000); } if(ql_mqtt_sub_unsub(&mqtt_cli, su_topic, 1, mqtt_requst_result_cb,NULL, 0) == MQTTCLIENT_WOUNDBLOCK){ mqtt_uart_log("=====wait unsubscrible result\r\n"); ql_rtos_semaphore_wait(mqtt_semp, 1000); } ql_rtos_task_sleep_ms(500); if(mqtt_connected == 1 && ql_mqtt_disconnect(&mqtt_cli, mqtt_disconnect_result_cb, NULL) == MQTTCLIENT_WOUNDBLOCK){ mqtt_uart_log("=====wait disconnect result\r\n"); ql_rtos_semaphore_wait(mqtt_semp, 1000);//QL_WAIT_FOREVER } mqtt_uart_log("==============mqtt_client_test[%d] end=======%ls=========\r\n",run_num,&mqtt_cli); ql_mqtt_client_deinit(&mqtt_cli); mqtt_connected = 0; ql_rtos_task_sleep_s(1); mqtt_state=STATE_MQTT_CFG; break; default: break; } } mqtt_uart_log("*-*-*-*-*Finished*-*-*-*-*\r\n"); exit: ql_rtos_task_delete(mqtt_task); return; } |
پس از کامپایل و نصب برنامه خروجی در سرور مانند زیرخواهد بود.
و خروجی داده های برنامه به صورت زیر است
درصورتی که موفق شدید گام بزرگی جهت ایجاد ارتباط مناسب جهت پروژه های IOT برداشتید. در ادامه سراغ Socket میرویم که اگرچه برای پروژه های کمی پیچیده به خوبی MQTT نیست اما به دلیل سادگی همچنان در پروژه های زیادی استفاده میشود.
برای استفاده از این قابلیت نیاز است که ابتدا از این مسیر (/components/ql-application/init/ql_init.c) تابع (ql_socket_app_init) را فعال کنید.
1 2 3 | #ifdef QL_APP_FEATURE_SOCKET ql_socket_app_init(); #endif |
سپس لازم است که آدرس سرور را در این قسمت از کد وارد کنید.
1 | #define QL_SOCKET_DEMO_URL "188.136.xxx.xxx" |
و در ادامه شماره Portی که میخواهید استفاده کنید را در این قسمت قراردهید.
1 | server_ipv4.sin_port = htons(10xx); |
بعد از کامپایل و نصب کد خروجی مانند زیر خواهید داشت که نشان دهنده ی ارتباط صحیح با سرور است.
باتوجه به اینکه هرروز در وب گردی با HTTP سروکار دارید حتما با پروتکل بعدی به خوبی آشنا هستید. این پروتکل برای نمایش داده ها مناسب است و کاربردهای متنوعی دارد.
برای استفاده از این پروتکل نیاز است که ابتدا از این مسیر (/components/ql-application/init/ql_init.c) تابع (ql_http_app_init) را از حالت کامنت خارج کنید.
1 2 3 4 5 6 | #ifdef QL_APP_FEATURE_HTTP ql_http_app_init(); // ql_http_post_app_init(); // ql_http_put_app_init(); // ql_https_get_app_init(); #endif |
در اینجا همانند چیزی که قبلا دیدید برای دریافت داده های برنامه روی UART از این کد استفاده میکنیم و اسم این تابع را با تابع اصلی برنامه جایگزین میکنیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #include "ql_pin_cfg.h" #include "ql_uart.h" #define UART_LOG_BUFF_SIZE 512 // Buffer size for formatted output void http_uart_log(const char *format, ...) { char buffer[UART_LOG_BUFF_SIZE]; // Local buffer va_list args; va_start(args, format); vsnprintf(buffer, sizeof(buffer) - 3, format, args); // Reserve space for "\r\n" va_end(args); strcat(buffer, "\r\n"); // Automatically add "\r\n" int ret = 0; ql_uart_config_s uart_cfg = {0}; uart_cfg.baudrate = QL_UART_BAUD_115200; uart_cfg.flow_ctrl = QL_FC_NONE; uart_cfg.data_bit = QL_UART_DATABIT_8; uart_cfg.stop_bit = QL_UART_STOP_1; uart_cfg.parity_bit = QL_UART_PARITY_NONE; ret = ql_uart_set_dcbconfig(QL_UART_PORT_1, &uart_cfg); ret = ql_pin_set_func(QL_UART2_TX_PIN, QL_UART2_TX_FUNC); ret = ql_pin_set_func(QL_UART2_RX_PIN, QL_UART2_RX_FUNC); ret = ql_uart_open(QL_UART_PORT_1); ret = ql_pin_set_func(QL_UART2_TX_PIN, QL_UART2_TX_FUNC); if (QL_UART_SUCCESS == ret) { ql_uart_get_dcbconfig(QL_UART_PORT_1, &uart_cfg); ql_uart_write(QL_UART_PORT_1, (unsigned char*)buffer, strlen(buffer)); } } |
و برای ارسال و دریافت درخواست به سرور از این قسمت کد استفاده میکنیم. دقت کنید که آدرس سرور را با چیزی که در کد است (http://188.136.XXX.XXX:1000/http/send.php) جایگزین کنید. چنانچه سرور مناسب ندارید میتوانید برای داشتن یک سرور از سایتهایی که سرورهای ابری فراهم میکنند استفاده کنید.
1 2 3 4 5 6 7 8 9 | case 1:{//test https get char url[] = "http://188.136.XXX.XXX:1000/http/send.php"; http_method = HTTP_METHOD_GET; ql_httpc_setopt(&http_demo_client.http_client, HTTP_CLIENT_OPT_METHOD, http_method); ql_httpc_setopt(&http_demo_client.http_client, HTTP_CLIENT_OPT_SSLCTXID, 1); ql_httpc_setopt(&http_demo_client.http_client, HTTP_CLIENT_OPT_SSL_VERIFY_LEVEL, HTTPS_VERIFY_NONE); ql_httpc_setopt(&http_demo_client.http_client, HTTP_CLIENT_OPT_URL, (char *)url); } break; |
پس از کامپایل و نصب خروجی مانند زیر خواهیم داشت.
امیدوارم از این آموزش استفاده کافی را برده باشید. منتظر آموزشهای بعدی باشید و با نظراتتون به ما انرژی بدید. ممنونم 🙂
وبسایت: http://www.arvidtek.com
www.arvidtek.com | گروه مهندسی آرویدتک | فعال حوزه الکترونیک و مخابرات | فروشگاه تخصصی قطعات الکترونیک
مقالات بیشتر
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.