کنترل وسایل از راه دور همیشه جزو جذابیت های الکترونیک بوده ، نمونه ای از این کار هم که متداول تر هست با کمک ماژول های gsm و پیامک انجام میشه ، حالا ما توی این آموزش می خوایم با کمک ماژول M66 شرکت کوییتل یک کنترل رله پیامکی بسازیم ، البته نه به همراه یک پردازنده خارجی (مثل آردوینو) ، بلکه به صورت OpenCpu استفاده از این روش باعث میشه تا هزینه سخت افزار ما پایین تر بیاد و برد کوچیک تری هم داشته باشیم ، پس با امبددتک همراه باشید تا به سراغ این پروژه محبوب بریم.
در ابتدا باید بگم که شما برای انجام این پروژه باید با کار با OpenCpu توی ماژول M66 آشنا باشید برای اینکار میتونید این قسمت آمورش رو بررسی کنید:
کار با ماژول M66 – قسمت اول – کار با OpenCpu
سخت افزار
برای سخت افزار ما از برد توسعه ماژول M66 استفاده کردیم و برای اون یک مین برد زدیم تا وارد بحث طراحی برد برای خود ماژول نشیم در ادامه تصویر این برد رو مشاهده میکنید:
شماتیک برد بالا رو میتونید از اینجا مشاهده کنید ، در ادامه بخش های مختلف شماتیک رو بررسی میکنیم:
تغذیه
ورودی برد ما 12 ولت هست و این 12 ولت مستقیم میره سر سیم پیچ های رله ، غیر از اون یه رگولاتور L7805 هم اینجا داریم تا ولتاژ مورد نیاز برای هدر برد M66 رو تامین کنه ، همچنین یه رگولاتور 3.3 ولت برای تامین تغذیه آیسی های PCF8574 و 7HC04 که در ادامه کاربرد اونها گفته میشه
بخش کنترل
قسمت اصلی ما اینجا خروجی I2C ماژول M66 هست که به آیسی PCF8574 میره تا از طریق اون خروجی های ما کنترل بشه ، هر چند بدون این آیسی هم ما میتونستیم خروجی ها رو کنترل کنیم (حداقل الان چون فقط 4 تا خروجی داریم) اما یه مزیت مهم داره استفاده از این آیسی ، مسئله اینجاست که این ماژول های GSM ممکنه برخی اوقات ریست بشند که دلایل مختلفی میتونه داشته باشه و میشه گفت طبیعی هست توی این ماژول ها ، اما نکته اینه که اگر ما مستقیم خروجی ها رو به پایه های ماژول متصل کنیم هنگام ریست شدن وضعیت پایه ها از دست میره یا دست کم یه بار قطع و وصل میشه که اتفاق جالبی نیست! بعد از اون خروجی آیسی PCF8574 رو به یک آیسی NOT میدیم که استفاده ازش دوتا دلیل داره ، یکی همون NOT کردن خروجی های PCF (چون برعکس هست خروجیش) و دیگری بافر کردن خروجی PCF که بتونیم به کمک اون ترانزیستور ها رو فعال کنیم.
کنترل خروجی ها
قسمت آخر هم اینجاست که خروجی آیسی NOT به یک ترانزیستور رفته و از اونجا رله رو فعال میکنه ، همچنین اینجا یک دیود هرزگرد و یک LED برای هر رله داریم.
کدنویسی
ابتدا خروجی کد رو بررسی میکنیم و سپس به بررسی قسمت های مختلف میپردازیم
اولین بار که ماژول روشن میشه از هیچ شماره ای دستور نمیگیره
بعد باید دکمه btn که روی برد هست رو 2 ثانیه نگه دارید تا سه تا led روی برد شروع به چشمک زدن بکنند.
حالا شماره اصلی که میخواید برد رو کنترل کنه باهاش یه پیام با محتوای “admin” بدید.
اگر پیام درست ارسال بشه چراغ ها از حالت چشمک زن در میاد.
و اون شماره به عنوان ادمین ذخیره میشه.
حالا چهار تا خروجی داریم.
با دستور
on out x
که به جای x از 1 تا 4 شماره خروجی رو مشخص میکنید
با دستور
off out x
هم خاموش میشه
و با دستور
pulse out x
خروجی لحظه ای فعال میشه
برای اضافه کردن کاربر هم از این دستور استفاده میشه
add user +989100000000
فقط فرمت شماره باید به همین صورت باشه
با دستور
user list
هم شماره کاربران برگردونده میشه
چهار تا شماره هم جمعا ذخیره میتونید بکنید
کد کامل این پروژه اینجا موجود هست ، ما به بررسی بخش های مختلف کد فایل main.c میپردازیم که نسخه کاملش رو میتونید از اینجا بررسی کنید
نقطه شروع کد ما تابع proc_main_task هست
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 | void proc_main_task(s32 iTaskID) { s32 iResult = 0; ST_MSG taskMsg; // Register & open UART port InitSerialPort(); APP_DEBUG("SMS Controller (mahdi2001h)\r\n"); iResult = Ql_IIC_Init(1, PINNAME_RI, PINNAME_DCD, FALSE); APP_DEBUG("IIC init %d\r\n", iResult); Ql_Sleep(100); iResult = Ql_IIC_Config(1, TRUE, 0x40, 100); APP_DEBUG("IIC Config %d\r\n", iResult); Data.all =0; // enable led Ql_GPIO_Init(OUT_1, PINDIRECTION_OUT, PINLEVEL_LOW, PINPULLSEL_PULLUP); Ql_GPIO_Init(OUT_2, PINDIRECTION_OUT, PINLEVEL_LOW, PINPULLSEL_PULLUP); Ql_GPIO_Init(OUT_3, PINDIRECTION_OUT, PINLEVEL_LOW, PINPULLSEL_PULLUP); Ql_GPIO_Init(IN_1, PINDIRECTION_IN, PINLEVEL_HIGH, PINPULLSEL_PULLUP); Ql_GPIO_SetLevel(OUT_1, PINLEVEL_HIGH); Ql_GPIO_SetLevel(OUT_2, PINLEVEL_HIGH); Ql_GPIO_SetLevel(OUT_3, PINLEVEL_HIGH); Ql_Sleep(200); Ql_GPIO_SetLevel(OUT_1, PINLEVEL_LOW); Ql_GPIO_SetLevel(OUT_2, PINLEVEL_LOW); Ql_GPIO_SetLevel(OUT_3, PINLEVEL_LOW); check_file(); ... |
این قسمت کار خاصی انجام نمیده فقط میاد I2C رو راه اندازی میکنه و یک بار ال ای دی های روی خود برد توسعه رو خاموش روشن میکنه ، در آخر تابع check_file صدا زده میشه این تابع میاد و بررسی میکنه که فایلی داخل UFS داخلی ماژول با نام data.txt موجود هست یا نه ، در واقع ما از این فایل برای ذخیره شماره هایی که امکان دسترسی دارند رو مشخص میکنیم
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | void check_file() { handle = Ql_FS_Open(DATA_FILE_PATH, QL_FS_READ_ONLY); if (handle > 0) { Ql_FS_Seek(handle, 0, QL_FS_FILE_BEGIN); Ql_FS_Read(handle, file_content, LENGTH - 1, &readenLen); Ql_FS_Close(handle); Ql_strncpy(users[0], file_content, 13); Ql_strncpy(users[1], file_content + 13, 13); Ql_strncpy(users[2], file_content + 26, 13); Ql_strncpy(users[3], file_content + 39, 13); APP_DEBUG("admin : %s\r\n user 1 : %s\r\n user 2 : %s\r\n user 3 : %s\r\n", users[0], users[1], users[2], users[3]); APP_DEBUG("file content :%s\r\n", file_content); } else { APP_DEBUG("file does not exist \r\n"); } } |
اگر که این فایل وجود داشت اطلاعات اون رو پردازش میکنه و در آرایه users میریزه.
دوباره به تابع proc_main_task بر میگردیم ،در ادامه یک حلقه while داریم که پیام های دریافتی از هسته ماژول رو دریافت میکنه که یکی از اونها دریافت SMS جدید هست ، توی خط 758 این اتفاق میوفته
1 2 3 4 5 6 | case URC_NEW_SMS_IND: { APP_DEBUG("\r\n<-- New SMS Arrives: index=%d\r\n", taskMsg.param2); Hdlr_RecvNewSMS((taskMsg.param2), FALSE); break; } |
اگر که پیام جدیدی دریافت شده بود تابع Hdlr_RecvNewSMS صدا زده میشه و اطاعات پیام هم به تابع ارسال میشه ، با قسمت های اول این تابع کاری نداریم و به خط 292 میرسیم
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 | if (mode == SET_ADMIN) { if (Ql_strcmp((pDeliverTextInfo->data), "admin") == 0) { APP_DEBUG("admin = %s\r\n", pDeliverTextInfo->oa); Ql_strcpy(users[0], pDeliverTextInfo->oa); SMS_TextMode_Send(users[0], "now you are admin"); mode = NORMAL; handle = Ql_FS_Open(DATA_FILE_PATH, QL_FS_CREATE_ALWAYS); if (handle > 0) { Ql_FS_Truncate(handle); Ql_FS_Flush(handle); Ql_FS_Seek(handle, 0, QL_FS_FILE_BEGIN); Ql_FS_Write(handle, users[0], Ql_strlen(users[0]), &readenLen); Ql_FS_Flush(handle); Ql_FS_Close(handle); } else { APP_DEBUG("ERROR WRITING File \r\n"); } } else { APP_DEBUG("not equal\r\n"); } } |
در اینجا بررسی میشه که آیا داخل حالت تنظیم admin هست یا نه (که این در 521 مشخص میشه و در صورتی که دکمه روی برد بیشتر از 2 ثانیه نگه داشته بشه )، اگر در این وضعیت بود شماره ای که از اون پیامک دریافت شده رو توی فایل data.txt ذخیره میکنه و فقط از اون دستور میگیره
در ادامه محتویات متن دریافتی از طریق پیامک بررسی میشه تا عمل مرتبط با اون انجام بشه ، توی خط 326 بررسی میشه که اگر پیامک با متن “add user” شروع شده بود شماره ای که وارد شده رو به لیست کاربر ها اضافه کنه
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 | if (Ql_strncmp((pDeliverTextInfo->data), "add user ", 9) == 0) { if (Ql_strncmp((pDeliverTextInfo->oa), users[0], 13) == 0) { if (Ql_strlen(pDeliverTextInfo->data) == 22) { handle = Ql_FS_Open(DATA_FILE_PATH, QL_FS_CREATE); if (handle > 0) { Ql_FS_Seek(handle, 0, QL_FS_FILE_BEGIN); Ql_FS_Read(handle, file_content, LENGTH - 1, &readenLen); APP_DEBUG("file = %s; len :%d\r\n", file_content, readenLen); if ((readenLen / 13) < 4) { APP_DEBUG("start write\r\n"); Ql_strcpy(users[(readenLen / 13)], (pDeliverTextInfo->data) + 9); Ql_FS_Seek(handle, 0, QL_FS_FILE_END); Ql_FS_Write(handle, (pDeliverTextInfo->data) + 9, Ql_strlen((pDeliverTextInfo->data) + 9), &readenLen); Ql_FS_Flush(handle); APP_DEBUG("end write\r\n"); } else { APP_DEBUG("max users\r\n"); SMS_TextMode_Send(pDeliverTextInfo->oa, "max users"); } Ql_FS_Close(handle); } } else { APP_DEBUG("incorrect phone number\r\n"); SMS_TextMode_Send(pDeliverTextInfo->oa, "incorrect phone number"); } } else { APP_DEBUG("You are not admin\r\n"); SMS_TextMode_Send(pDeliverTextInfo->oa, "You are not admin"); } } |
در ادامه در خط 368 بررسی میشه که اگر دستور “user list” اومد لیست شماره هایی که میتونند دستور ارسال کنند رو برگردونه
1 2 3 4 5 6 7 8 | if (Ql_strcmp((pDeliverTextInfo->data), "user list") == 0) { char temp[100]; Ql_sprintf(temp, "admin : %s\r\n user 1 : %s\r\n user 2 : %s\r\n user 3 : %s\r\n\0", users[0], users[1], users[2], users[3]); APP_DEBUG("admin : %s\r\n user 1 : %s\r\n user 2 : %s\r\n user 3 : %s\r\n", users[0], users[1], users[2], users[3]); SMS_TextMode_Send(users[0], temp); } |
بعد از اون میرسیم به قسمت اصلی کارمون یعنی اگر دستور با “on out” شروع شد
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 | if (Ql_strncmp((pDeliverTextInfo->data), "on out ", 7) == 0) { APP_DEBUG("on = %d\r\n", pDeliverTextInfo->data[7]); if (pDeliverTextInfo->data[7] > 47 && pDeliverTextInfo->data[7] < 58) { switch (pDeliverTextInfo->data[7] - 48) { case 1: Data.set.out_1 = 1; break; case 2: Data.set.out_2 = 1; break; case 3: Data.set.out_3 = 1; break; case 4: Data.set.out_4 = 1; break; case 5: Data.set.out_5 = 1; break; case 6: Data.set.out_6 = 1; break; default: break; } update_IO(); } } |
ابتدا در خط 380 بررسی میشه که بعد از دستور “on out” حتما یه عدد اومده باشه و نه کاراکتر یا چیز دیگه ای
1 | if (pDeliverTextInfo->data[7] > 47 && pDeliverTextInfo->data[7] < 58) |
بعد از اون هم اگر مثلا عدد 1 اومده بود ، بیت اول متغیر all رو برابر 1 میکنه و در انتها تابع update_IO صدا زده میشه که دیتا ها رو برای PCF میفرسته تا اعمال بشند.
منبع : سیسوگ
واقعا پروژه ساده و خوبیه. من برای آبیاری و چندتا چراغ گرفتم.
فقط کاش یه ذره کامل تر میشد. مثلا وضعیت رله ها با ارسال دستور status به شماره درخواست دهنده ارسال میشد که بدونیم مثلا بعد 1 هفته کدوما روشنه کدوما خاموش. یا مثلا درستور all off و all on اضافه میشد. اگر case sensetive بودنش هم برطرف میشد خوب بود.
در رابطه با PCB هم بهتر بود یه مقدار کلیرنس بیشتر میشد و GND در چندجا با via هر دو لایه رو به هم متصل میکرد.
در حال حاضر خوبه و کار میکنه ولی معلوم نیست کی میبره توی چه محیطی، یه کم قابلیت اطمینان اینطوری بیشتر میشد.
در کل ممنون که کلی وقت سیو شد برام که نشستم خودم درست کنم.
ممنون از شما
این پروژه برای یادگیری انجام شده و با توجه به اینکه کد و pcb اون به صورت اوپن سورس منتشر شده ، شما میتونید به راحتی اون رو بر اساس نیازتون تغییر بدید