Micropython على GSM + GPS وحدة A9G

هذه المرة فكرت في إخفاء جهاز تعقب GPS في دراجتي كإجراء وقائي. هناك الكثير من الأجهزة المستقلة في السوق لتتبع السيارات والبضائع والدراجات والأمتعة والأطفال والحيوانات. تتفاعل الغالبية العظمى منهم مع المستخدم عبر الرسائل القصيرة. توفر خيارات أكثر تكلفة العثور على وظيفة هاتفي ، ولكن مرتبطة بخدمة معينة عبر الإنترنت.
من الناحية المثالية ، أود التحكم الكامل في برنامج التعقب: استخدمه في وضع مناسب بدون رسائل نصية قصيرة وتسجيل. أحضر لي جوجل السطحي بضعة وحدات من الصين ، واحدة منها طلبت (بودنج A9G) (حوالي 15 دولارًا).


وحدة


هذا المقال يدور حول كيفية قيامي بيثون بالعمل على هذه الوحدة.


إذا كانت A9G تمثيلية لـ ESP (الشركة المصنعة ، بالمناسبة ، هي نفسها) ، فإن لوحة البودنج نفسها تمثيلية للوحة NodeMCU ، باستثناء أن لوحة البودنج لا تحتوي على محول USB-UART مضمن. ولكن هناك العديد من الأشياء الأخرى المثيرة للاهتمام. مواصفات المصنع :


  • 32 بت الأساسية (RISC) ، وتصل إلى 312MHz
  • 29x GPIO (جميعها ملحومة ، جميع الواجهات مدرجة في هذا الرقم)
  • الساعات والوكالة الدولية للطاقة
  • 1x واجهة USB 1.1 (لم أجدها هناك ، ولكن نسخ من خارج الموقع) و microUSB للحصول على الطاقة
  • UART 2x (خدمة +1)
  • 2X SPI (لم تتم محاكمته)
  • 3X I2C (لم يحاكم)
  • 1MM SDMMC (مع فتحة المادية)
  • مدخلات تناظرية 2x (10 بتات ، ربما يتم استخدام واحدة منها بواسطة أدوات التحكم في بطارية الليثيوم)
  • 4 ميغابايت فلاش
  • 4Mb PSRAM
  • ADC (ميكروفون ، موجود فعليًا على اللوحة) و DAC (مكبر صوت ، غائب)
  • وحدة تحكم شحن البطارية (لا توجد بطارية في حد ذاتها)
  • في الواقع ، GSM (800 ، 900 ، 1800 ، 1900 ميغاهيرتز) مع الرسائل القصيرة والصوت و GPRS
  • اتصال GPS عبر UART2 (توجد وحدة "A9" بدونها)
  • فتحة SIM (nanoSIM)
  • زرين (إعادة تعيين واحد ، والآخر - وظيفة التضمين والبرمجة)
  • اثنين من المصابيح

الجهد التشغيلي هو 3.3 فولت ، جهد الدخل هو 5-3.8 فولت (اعتمادا على الاتصال). بشكل عام ، تحتوي الوحدة على جميع الأجهزة اللازمة لتجميع جهاز محمول بسيط يعمل بالضغط على الزر. ولكن من الأمثلة ، يبدو أن الصينيين يشترونها للبيع من ماكينات القمار أو ماكينات القمار أو شيء من هذا القبيل. بدائل الوحدة النمطية هي وحدات SIM800 الشائعة إلى حد ما ، والتي ، للأسف ، لا تملك SDK في المجال العام (على سبيل المثال ، تُباع الوحدات كوحدات مودم AT).


SDK


الوحدة يأتي مع SDK في اللغة الإنجليزية مرضية. التثبيت تحت Ubuntu ، ولكن يفضل استخدام Windows والحاويات. كل شيء يعمل من خلال بدس في واجهة المستخدم الرسومية: ESPtool لهذه الوحدة لم يتم إرجاعها بعد. تم بناء البرامج الثابتة نفسها بواسطة Makefile. مصحح الأخطاء موجود: قبل التجميد ، تقوم الوحدة بإلقاء تتبع المكدس في منفذ الخدمة. لكن شخصياً ، لم أتمكن من ترجمة العناوين إلى سطور من التعليمات البرمجية (تقارير gdb أن العناوين لا تتوافق مع أي شيء). من المحتمل أن يكون هذا بسبب الدعم الضعيف لنظام Linux على هذا النحو. وفقًا لذلك ، إذا كنت تريد العبث بالوحدة النمطية - فحاول القيام بذلك تحت Windows (وقم بإلغاء الاشتراك في github). خلاف ذلك ، وهنا هو تعليمات لينكس. بعد التثبيت ، تحتاج إلى التحقق من صحة المسارات في .bashrc وحذف (إعادة تسمية) جميع CSDTK/lib/libQt* : وإلا ، فلن يبدأ المتعري (المعروف أيضًا باسم مصحح الأخطاء) بسبب تعارض مع ، ربما ، libQt المثبت.


المتعري


إلى المتعري هناك تعليمات .


صلة


كل شيء أكثر تعقيدًا من NodeMCU. تبدو الوحدات متشابهة ، ولكن لا توجد رقاقة USB-TTY على لوحة الحلوى ويستخدم microUSB فقط من أجل الطاقة. وفقا لذلك ، سوف تحتاج USB-TTY في 3.3V. اثنان أفضل: واحد لمنفذ التصحيح والآخر لـ UART1: الأول يُستخدم لتحميل البرامج الثابتة والثاني يمكنك استخدامه كطرف منتظم. لكي لا اسحب كل هذه الأجزاء إلى الكمبيوتر ، اشتريت بالإضافة إلى ذلك موزع USB 4 منافذ مع كابل بطول مترين ومزود طاقة خارجي (مطلوب). التكلفة الإجمالية لهذه المجموعة مع الوحدة نفسها ستكون 25-30 دولار (بدون امدادات الطاقة: استخدام من الهاتف).


إدراج


تأتي الوحدة مع البرامج الثابتة AT: يمكنك الاتصال بـ arduino 3.3V واستخدامه كمودم عبر UART1. تمت كتابة البرامج الثابتة الخاصة بهم في C. make بإنشاء ملفين من البرامج الثابتة: أحدهما مخيط لمدة دقيقة ، والآخر سريع بما فيه الكفاية. يمكن خياطة ملف واحد فقط من هذه الملفات: المرة الأولى كبيرة ، والأوقات اللاحقة صغيرة. إجمالاً ، أثناء عملية التطوير ، لديّ SDK Chinese ( coolwatcher ) مفتوح على سطح المكتب لإدارة الوحدة النمطية ، والصغير ، stdio ومحرر الشفرة.


API


يعكس محتوى واجهة برمجة التطبيقات القائمة أعلاه ويشبه ESP8266 في الأيام الأولى: استغرق الأمر حوالي 3 ساعات لإطلاق HelloWorld. لسوء الحظ ، فإن مجموعة الوظائف المتاحة للمستخدم محدودة للغاية: على سبيل المثال ، لا يمكن الوصول إلى دفتر الهاتف على بطاقة SIM ، والمعلومات ذات المستوى المنخفض حول الاتصال بالشبكة الخلوية ، وما إلى ذلك. وثائق API أقل اكتمالا ، لذلك يجب عليك الاعتماد على أمثلة (والتي يوجد منها 24) وتضمين الملفات. ومع ذلك ، يمكن للوحدة القيام بالكثير من الأشياء حتى اتصالات طبقة المقابس الآمنة: من الواضح أن الشركة المصنعة ركزت على الوظائف الأكثر أولوية.


ومع ذلك ، يجب أن يكون محبوب برمجة ميكروكنترولر الصينية من خلال API الصينية. بالنسبة للجميع ، بدأت الشركة المصنعة في تحويل micropython إلى هذه الوحدة. قررت أن أجرب نفسي في مشروع مفتوح المصدر وأواصل هذا العمل الجيد (رابط في نهاية المقال).


micropython


شعار


Micropython هو مشروع مفتوح المصدر ينقل cPython إلى ميكروكنترولر. يتم تنفيذ التنمية في اتجاهين. الأول هو دعم وتطوير المكتبات الأساسية المشتركة بين جميع المتحكمات الدقيقة التي تصف العمل مع أنواع البيانات الرئيسية في بيثون: الكائنات ، الوظائف ، الفئات ، السلاسل ، الأنواع الذرية ، وأكثر من ذلك. والثاني هو ، في الواقع ، المنافذ: لكل متحكم ، من الضروري "تعليم" المكتبة للعمل مع UART من أجل المدخلات والمخرجات ، وتحديد رصة لجهاز افتراضي ، وتحديد مجموعة من التحسينات. اختياريا ، يتم وصف العمل مع الأجهزة: GPIO ، الطاقة ، اللاسلكي ، نظام الملفات.
كل هذا مكتوب بلغة C نقية مع وحدات ماكرو: يحتوي micropython على مجموعة من الوصفات الموصى بها من الإعلان عن الجمل في ROM إلى وحدات الكتابة. بالإضافة إلى ذلك ، يتم دعم وحدات python المكتوبة ذاتيا بالكامل (الشيء الرئيسي هو عدم نسيان حجم الذاكرة). حدد القيمون على المشروع كهدفهم الفرصة لإطلاق dzhanga (صورة مع رغيف الخبز). كإعلان: يبيع المشروع اللوحة الخاصة به لطلاب لوحة المعلومات ، لكن منافذ وحدات ESP8266 و ESP32 شائعة أيضًا.


عندما تكون البرامج الثابتة جاهزة وتحميلها - يمكنك فقط الاتصال بالمراقب الدقيق عبر UART والدخول إلى Python REPL.


 $ miniterm.py /dev/ttyUSB1 115200 --raw MicroPython cd2f742 on 2017-11-29; unicorn with Cortex-M3 Type "help()" for more information. >>> print("hello") hello 

بعد ذلك ، يمكنك البدء في الكتابة في python3 العادي تقريبًا دون نسيان قيود الذاكرة.


وحدة A9G غير مدعومة رسميًا (قائمة الوحدات المدعومة رسميًا متوفرة في micropython/ports ، وهناك حوالي عشرة منها). ومع ذلك ، قام صانع الحديد بتشكيل micropython وخلق البيئة لمنفذ micropython/ports/gprs_a9 : micropython/ports/gprs_a9 ، والذي شكره جزيلاً عليه. في الوقت الذي أصبحت فيه مهتمًا بهذه المشكلة ، تم تجميع المنفذ بنجاح واستقبلني المتحكم الصغير بـ REPL. ولكن ، لسوء الحظ ، لم يكن هناك عمل من خلال الوحدات التابعة لجهات خارجية إلا مع نظام الملفات و GPIO: لم يكن هناك شيء متعلق بالشبكة اللاسلكية ونظام تحديد المواقع العالمي (GPS). قررت إصلاح هذا العيب وحددت نفسي هدف نقل جميع الوظائف اللازمة لتعقب GPS. الوثائق الرسمية لهذه الحالة هي موجزة لا داعي لها: لذلك ، كان لا بد لي من البحث في الشفرة.


من أين تبدأ


أولاً ، انتقل إلى micropython/ports ونسخ micropython/ports/minimal إلى المجلد الجديد حيث سيتم وضع المنفذ. بعد ذلك ، قم بتحرير main.c الأساسي. ضع في اعتبارك أن كل لذيذ هو في الوظيفة main ، حيث تحتاج إلى استدعاء mp_init() ، بعد أن أعدت مسبقا متحكم وإعدادات المكدس لذلك. بعد ذلك ، بالنسبة لواجهة برمجة التطبيقات pyexec_event_repl_init() على الأحداث ، تحتاج إلى استدعاء pyexec_event_repl_init() وإطعام الأحرف التي تم إدخالها عبر UART إلى pyexec_event_repl_process_char(char) . سيوفر هذا إمكانية التشغيل المتداخل من خلال REPL. micropython/ports/minimal/uart_core.c الملف الثاني ، micropython/ports/minimal/uart_core.c حظر الإدخال والإخراج في UART. أحمل الرمز الأصلي ل STM32 لأولئك الذين كسول جدا للبحث عنها.


main.c


 int main(int argc, char **argv) { int stack_dummy; stack_top = (char*)&stack_dummy; #if MICROPY_ENABLE_GC gc_init(heap, heap + sizeof(heap)); #endif mp_init(); #if MICROPY_ENABLE_COMPILER #if MICROPY_REPL_EVENT_DRIVEN pyexec_event_repl_init(); for (;;) { int c = mp_hal_stdin_rx_chr(); if (pyexec_event_repl_process_char(c)) { break; } } #else pyexec_friendly_repl(); #endif //do_str("print('hello world!', list(x+1 for x in range(10)), end='eol\\n')", MP_PARSE_SINGLE_INPUT); //do_str("for i in range(10):\r\n print(i)", MP_PARSE_FILE_INPUT); #else pyexec_frozen_module("frozentest.py"); #endif mp_deinit(); return 0; } 

uart_core.c


 // Receive single character int mp_hal_stdin_rx_chr(void) { unsigned char c = 0; #if MICROPY_MIN_USE_STDOUT int r = read(0, &c, 1); (void)r; #elif MICROPY_MIN_USE_STM32_MCU // wait for RXNE while ((USART1->SR & (1 << 5)) == 0) { } c = USART1->DR; #endif return c; } // Send string of given length void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { #if MICROPY_MIN_USE_STDOUT int r = write(1, str, len); (void)r; #elif MICROPY_MIN_USE_STM32_MCU while (len--) { // wait for TXE while ((USART1->SR & (1 << 7)) == 0) { } USART1->DR = *str++; } #endif } 

بعد ذلك ، تحتاج إلى إعادة كتابة Makefile باستخدام توصيات / برنامج التحويل البرمجي من الشركة المصنعة: كل شيء فردي هنا. كل شيء ، يجب أن يكون هذا مثاليًا: نجمع ونملأ البرامج الثابتة ونرى REPL في UART.
بعد إحياء micropython تحتاج إلى الاهتمام micropython : إعداد أداة تجميع مجمعي البيانات المهملة ، ورد الفعل الصحيح على Ctrl-D (إعادة mpconfigport.h الناعم) وبعض الأشياء الأخرى التي لن mpconfigport.h : انظر ملف mpconfigport.h .


إنشاء وحدة نمطية


الشيء الأكثر إثارة للاهتمام هو كتابة الوحدات الخاصة بك. لذلك ، تبدأ الوحدة النمطية (غير ضرورية ، ولكنها مرغوب فيها) بملف mod[].c ، والذي تتم إضافته بواسطة Makefile (متغير SRC_C إذا اتبعت الاتفاقية). وحدة فارغة هي كما يلي:


 // nlr - non-local return:  C  ,      goto-  . //  nlr_raise             . #include "py/nlr.h" //   .  ,  mp_map_elem_t,  ,   . #include "py/obj.h" //   . mp_raise_ValueError(char* msg)  mp_raise_OSError(int errorcode)   . //  ,   mp_call_function_*     Callable (  callback-). #include "py/runtime.h" #include "py/binary.h" //  header   :       #include "portmodules.h" //    --  .     MP_QSTR_[ ]. MP_OBJ_NEW_QSTR   . //             RAM. //      -      __name__ STATIC const mp_map_elem_t mymodule_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_mymodule) }, }; //      STATIC MP_DEFINE_CONST_DICT (mp_module_mymodule_globals, mymodule_globals_table); //   :             const mp_obj_module_t mp_module_mymodule = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_mymodule_globals, }; 

بالطبع ، لا يتعرف المنفذ نفسه على ثابت mp_module_mymodule : يجب إضافته إلى متغير MICROPY_PORT_BUILTIN_MODULES في mpconfigport.h منفذ mpconfigport.h . بالمناسبة خلفيات مملة اسم رقاقة واسم المنفذ أيضا تغيير هناك. بعد كل هذه التغييرات ، يمكنك محاولة ترجمة الوحدة النمطية واستيرادها من REPL. __name__ سمة __name__ واحدة فقط مع اسم الوحدة النمطية متاحة للوحدة النمطية (وهي حالة رائعة للتحقق من الإكمال التلقائي في REPL عبر Tab).


 >>> import mymodule >>> mymodule.__name__ 'mymodule' 

الثوابت


المرحلة التالية من التعقيد تضيف الثوابت. غالبًا ما تكون الثوابت ضرورية للإعدادات ( INPUT ، OUTPUT ، HIGH ، LOW ، إلخ.) كل شيء بسيط جدًا هنا. هنا ، على سبيل المثال ، magic_number = 10 الثابت magic_number = 10 :


 STATIC const mp_map_elem_t mymodule_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_mymodule) }, { MP_OBJ_NEW_QSTR(MP_QSTR_magic_number), MP_OBJ_NEW_SMALL_INT(10) }, }; 

الاختبار:


 >>> import mymodule >>> mymodule.magic_number 10 

وظائف


تتبع إضافة وظيفة إلى وحدة نمطية المبدأ العام: التصريح ، الالتفاف ، الإضافة (أعطي مثالًا أكثر تعقيدًا بقليل مما في الوثائق).


 //  STATIC mp_obj_t conditional_add_one(mp_obj_t value) { //   int.         -  :   . int value_int = mp_obj_get_int(value); value_int ++; if (value_int == 10) { //  None return mp_const_none; } //   int return mp_obj_new_int(value); } //    .     // runtime.h   . STATIC MP_DEFINE_CONST_FUN_OBJ_1(conditional_add_one_obj, conditional_add_one); //  STATIC const mp_map_elem_t mymodule_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_mymodule) }, { MP_OBJ_NEW_QSTR(MP_QSTR_magic_number), MP_OBJ_NEW_SMALL_INT(10) }, { MP_OBJ_NEW_QSTR(MP_QSTR_conditional_add_one), (mp_obj_t)&conditional_add_one_obj }, }; 

الاختبار:


 >>> import mymodule >>> mymodule.conditional_add_one(3) 4 >>> mymodule.conditional_add_one(9) >>> 

فئات (أنواع)


مع الفئات (أنواع) ، كل شيء بسيط أيضًا. فيما يلي مثال من الوثائق (جيدًا ، تقريبًا):


 //     STATIC const mp_map_elem_t mymodule_hello_locals_dict_table[] = {}; //   STATIC MP_DEFINE_CONST_DICT(mymodule_hello_locals_dict, mymodule_hello_locals_dict_table); // ,  ,   const mp_obj_type_t mymodule_helloObj_type = { //    { &mp_type_type }, // : helloObj .name = MP_QSTR_helloObj, //  .locals_dict = (mp_obj_dict_t*)&mymodule_hello_locals_dict, }; //    STATIC const mp_map_elem_t mymodule_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_mymodule) }, { MP_OBJ_NEW_QSTR(MP_QSTR_magic_number), MP_OBJ_NEW_SMALL_INT(10) }, { MP_OBJ_NEW_QSTR(MP_QSTR_conditional_add_one), (mp_obj_t)&conditional_add_one_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_conditional_add_one), (mp_obj_t)&mymodule_helloObj_type }, }; 

الاختبار:


 >>> mymodule.helloObj <type 'helloObj'> 

يمكن أن يتم توريث النوع الناتج ، مقارنة ، لكنه لا يحتوي على مُنشئ أو أي بيانات مرتبطة به. تتم إضافة البيانات "بجانب" المُنشئ: يُقترح إنشاء هيكل منفصل يتم فيه تخزين نوع بيثون بشكل منفصل ومنفصل - مجموعة بيانات تعسفية.


 //  -. ,    typedef struct _mymodule_hello_obj_t { //   mp_obj_base_t base; // -  uint8_t hello_number; } mymodule_hello_obj_t; 

كيف تتفاعل مع هذه البيانات؟ واحدة من أصعب الطرق هي من خلال المنشئ.


 // -,   (,  ,   mymodule_helloObj_type //   ,     - ),   (args  kwargs)  //        : args, kwargs STATIC mp_obj_t mymodule_hello_make_new( const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args ) { //    mp_arg_check_num(n_args, n_kw, 1, 1, true); //   mymodule_hello_obj_t *self = m_new_obj(mymodule_hello_obj_t); //     self->base.type = &mymodule_hello_type; //   self->hello_number = mp_obj_get_int(args[0]) //   return MP_OBJ_FROM_PTR(self); //    __init__, ,  } //      make_new const mp_obj_type_t mymodule_helloObj_type = { { &mp_type_type }, .name = MP_QSTR_helloObj, .locals_dict = (mp_obj_dict_t*)&mymodule_hello_locals_dict, //  .make_new = mymodule_hello_make_new, }; 

من الحقول الأخرى ، هناك أيضًا .print ، وأعتقد أن ما تبقى من سحر Python3 .


لكن make_new ليس ضروريًا على الإطلاق للحصول على مثيل لكائن: يمكن إجراء التهيئة في وظيفة تعسفية. فيما يلي مثال جيد من micropython/ports/esp32/modsocket.c :


 //   :       STATIC mp_obj_t get_socket(size_t n_args, const mp_obj_t *args) { socket_obj_t *sock = m_new_obj_with_finaliser(socket_obj_t); sock->base.type = &socket_type; sock->domain = AF_INET; sock->type = SOCK_STREAM; sock->proto = 0; sock->peer_closed = false; if (n_args > 0) { sock->domain = mp_obj_get_int(args[0]); if (n_args > 1) { sock->type = mp_obj_get_int(args[1]); if (n_args > 2) { sock->proto = mp_obj_get_int(args[2]); } } } sock->fd = lwip_socket(sock->domain, sock->type, sock->proto); if (sock->fd < 0) { exception_from_errno(errno); } _socket_settimeout(sock, UINT64_MAX); return MP_OBJ_FROM_PTR(sock); } //     0-3  STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(get_socket_obj, 0, 3, get_socket); 

طرق ملزمة


والخطوة التالية هي إضافة الأساليب المنضمة. ومع ذلك ، هذا لا يختلف كثيرا عن جميع الأساليب الأخرى. نعود إلى المثال من الوثائق:


 //    :     1 (self) STATIC mp_obj_t mymodule_hello_increment(mp_obj_t self_in) { mymodule_hello_obj_t *self = MP_OBJ_TO_PTR(self_in); self->hello_number += 1; return mp_const_none; } //     MP_DEFINE_CONST_FUN_OBJ_1(mymodule_hello_increment_obj, mymodule_hello_increment); //      'inc' STATIC const mp_map_elem_t mymodule_hello_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_inc), (mp_obj_t)&mymodule_hello_increment_obj }, } 

هذا كل شئ!


 >>> x = mymodule.helloObj(12) >>> x.inc() 

جميع السمات الأخرى: getattr ، setattr


ماذا عن إضافة غير وظائف ، باستخدام @property وعموما __getattr__ الخاصة بك؟ يرجى: أن يتم ذلك يدويا تجاوز mymodule_hello_locals_dict_table .


 //     ... STATIC void mymodule_hello_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mymodule_hello_obj_t *self = MP_OBJ_TO_PTR(self_in); if (dest[0] != MP_OBJ_NULL) { // __setattr__ if (attr == MP_QSTR_val) { self->val = dest[1]; dest[0] = MP_OBJ_NULL; } } else { // __getattr__ if (attr == MP_QSTR_val) { dest[0] = self->val; } } } // ...     attr const mp_obj_type_t mymodule_helloObj_type = { { &mp_type_type }, .name = MP_QSTR_helloObj, //     //.locals_dict = (mp_obj_dict_t*)&mymodule_hello_locals_dict, .make_new = mymodule_hello_make_new, //   - attr .attr = mymodule_hello_attr, }; 

تحولت شيئا موجزا مؤلم attr ، كما تقول. أين كل هذه mp_raise_AttributeError ( ملاحظة : هذه الوظيفة غير موجودة)؟ في الواقع ، سيتم استدعاء AttributeError تلقائيًا. السر هو أن dest هو مجموعة من عنصرين. يحتوي العنصر الأول على معنى "الإخراج" ، للكتابة فقط: يستغرق القيمة MP_OBJ_SENTINEL إذا كانت القيمة تحتاج إلى كتابة و MP_OBJ_NULL إذا كانت بحاجة إلى قراءتها. وفقًا لذلك ، عند الخروج من الوظيفة ، من المتوقع MP_OBJ_NULL في الحالة الأولى وشيء mp_obj_t في الحالة الثانية. العنصر الثاني هو الإدخال ، للقراءة فقط: يأخذ قيمة الكائن في الكتابة إذا كانت القيمة تحتاج إلى كتابة و MP_OBJ_NULL إذا كانت بحاجة إلى القراءة. لا تحتاج إلى تغييره.


هذا كل شيء ، يمكنك التحقق من:


 >>> x = mymodule.helloObj(12) >>> x.val = 3 >>> x.val 3 

الأكثر إثارة للاهتمام هو أن إكمال علامة التبويب في REPL لا يزال يعمل ويقدم .val ! لكي أكون أمينًا ، لست خبيرًا في C ، لذلك لا يمكنني إلا أن أخمن كيف يحدث ذلك (عن طريق إعادة تعريف المشغل '==').


ميناء


بالعودة إلى وحدة A9G ، وصفت دعم جميع الوظائف الأساسية ، وهي SMS و GPRS (usockets) و GPS وإدارة الطاقة. الآن يمكنك تحميل شيء مثل هذا إلى الوحدة النمطية وسيعمل:


 import cellular as c import usocket as sock import time import gps import machine #   print("Waiting network registration ...") while not c.is_network_registered(): time.sleep(1) time.sleep(2) #  GPRS print("Activating ...") c.gprs_activate("internet", "", "") print("Local IP:", sock.get_local_ip()) #  GPS gps.on() #    thingspeak host = "api.thingspeak.com" api_key = "some-api-key" fields = ('latitude', 'longitude', 'battery', 'sat_visible', 'sat_tracked') #  ,      ! fields = dict(zip(fields, map(lambda x: "field{}".format(x+1), range(len(fields))) )) x, y = gps.get_location() level = machine.get_input_voltage()[1] sats_vis, sats_tracked = gps.get_satellites() s = sock.socket() print("Connecting ...") s.connect((host, 80)) print("Sending ...") #      ,     HTTP.           HTTP, SSL   print("Sent:", s.send("GET /update?api_key={}&{latitude}={:f}&{longitude}={:f}&{battery}={:f}&{sat_visible}={:d}&{sat_tracked}={:d} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n".format( api_key, x, y, level, sats_vis, sats_tracked, host, **fields ))) print("Receiving ...") print("Received:", s.recv(128)) s.close() 

يرحب المشروع بأي مساعدة ممكنة. إذا كنت تحب المشروع و / أو هذه المقالة ، لا تنسى أن تترك مثل على جيثب .

Source: https://habr.com/ru/post/ar446090/


All Articles