GSM + GPSモゞュヌルA9G䞊のMicropython

今回は、予防措眮ずしお自転車にGPSトラッカヌを隠すこずを考えたした。 垂堎には、車、貚物、自転車、荷物、子䟛、動物を远跡するための倚数の自埋型デバむスがありたす。 それらの倧郚分は、SMSを介しおナヌザヌず察話したす。 より高䟡なオプションは、電話怜玢機胜を提䟛したすが、特定のオンラむンサヌビスに関連付けられおいたす。

理想的には、トラッカヌを完党に制埡したいず思いたす。SMSず登録なしで䟿利なモヌドでトラッカヌを䜿甚しおください。 衚面的なグヌグルは私に䞭囜からいく぀かのモゞュヌルを持っおきお、そのうちの䞀぀を泚文したしたA9Gプリンボヌド〜$ 15。







モゞュヌル







この蚘事では、このモゞュヌルでpythonを動䜜させる方法に぀いお説明したす。







A9GがESPのアナログである堎合補造元は同じです、プリンティングボヌド自䜓はNodeMCUボヌドのアナログです。ただし、プリンティングボヌドにはUSB-UARTコンバヌタヌが組み蟌たれおいない点が異なりたす。 しかし、他にも倚くの興味深いこずがありたす。 メヌカヌ仕様









動䜜電圧は3.3V、入力電圧は5〜3.8Vです接続によっお異なりたす。 䞀般に、モゞュヌルには、シンプルなプッシュボタンモバむルデバむスを組み立おるために必芁なすべおのハヌドりェアが含たれおいたす。 しかし、䟋から、䞭囜人はそれをスロットマシンたたはスロットマシンなどから販売するために賌入しおいるようです。 モゞュヌルの代替品は非垞に人気のあるSIM800モゞュヌルで、残念ながらパブリックドメむンにSDKがありたせん぀たり、モゞュヌルはATモデムずしお販売されおいたす。







SDK



モゞュヌルには、満足のいく英語のSDKが付属しおいたす 。 Ubuntuの䞋にむンストヌルしたすが、Windowsずコンテナが優先されたす。 GUIでの確認によっおすべおが機胜したす。このモゞュヌルのESPtoolはただ元に戻されおいたせん。 ファヌムりェア自䜓はMakefileによっお構築されたす。 デバッガヌが存圚したす。フリヌズする前に、モゞュヌルはスタックトレヌスをサヌビスポヌトにスロヌしたす。 しかし、個人的には、アドレスをコヌド行に倉換できたせんでしたgdbは、アドレスが䜕にも察応しおいないず報告しおいたす。 これは、Linuxのサポヌトが䞍十分であるこずが原因である可胜性がありたす。 したがっお、モゞュヌルをいじりたい堎合は、Windowsで実行しおみおくださいそしおgithubでサブスクラむブを解陀したす。 それ以倖の堎合は、Linuxの手順を以䞋に瀺したす。 むンストヌル埌、.bashrcのパスの正確さを確認し、すべおのCSDTK/lib/libQt*



を削陀名前倉曎する必芁がありたす。







フラッシャヌ







フラッシャヌに指瀺がありたす 。







接続



すべおがNodeMCUよりも耇雑です。 モゞュヌルは䌌おいたすが、プディングボヌドにはUSB-TTYチップがなく、microUSBは電源にのみ䜿甚されたす。 したがっお、3.3VのUSB-TTYが必芁です。 デバッグポヌト甚ずUART1甚の2぀が優れおいたす。1぀目はファヌムりェアのアップロヌドに䜿甚され、2぀目は通垞の端末ずしお䜿甚できたす。 これらのすべおの錻氎をコンピュヌタヌにドラッグしないために、2メヌトルのケヌブルず倖郚電源を備えた4ポヌトUSBスプリッタヌを远加で賌入したした必須。 モゞュヌル自䜓を含むこのキットの合蚈費甚は25〜30ドルです電源なし電話からの䜿甚。







ファヌムりェア



モゞュヌルにはATファヌムりェアが付属しおいたす。3.3Varduinoに接続し、UART1経由でモデムずしお䜿甚できたす。 ファヌムりェアはCで蚘述されおいたす。makeは2぀のファヌムりェアファむルmake



䜜成したす。1぀は玄1分間瞫い付けられ、もう1぀は十分高速です。 これらのファむルの1぀だけを瞫うこずができたす。最初の時間は倧きく、それ以降の時間は小さくなりたす。 党䜓ずしお、開発プロセス䞭に、モゞュヌル、stdioずしおのミニタヌム、およびデスクトップ䞊のコヌド゚ディタヌを管理するために、䞭囜語SDK coolwatcher



をcoolwatcher



おいたす。







API



APIの内容は䞊郚のリストを反映しおおり、初期のESP8266に䌌おいたす。HelloWorldを起動するのに玄3時間かかりたした。 残念ながら、ナヌザヌが䜿甚できる機胜のセットは非垞に限られおいたす。たずえば、SIMカヌドの電話垳ぞのアクセス、セルラヌネットワヌクぞの接続に関する䜎レベルの情報などがありたせん。 APIドキュメントの完成床はさらに䜎いため、サンプルそのうちの12個に䟝存し、ファむルをむンクルヌドする必芁がありたす。 それにもかかわらず、モゞュヌルはSSL接続たで倚くのこずを実行できたす。明らかに、補造業者は最も優先床の高い機胜に焊点を合わせたした。







ただし、䞭囜のAPIを介した䞭囜のマむクロコントロヌラヌのプログラミングは愛されなければなりたせん。 他のすべおの人にずっお、メヌカヌはこのモゞュヌルにmicropythonを移怍し始めたした 。 私はオヌプン゜ヌスプロゞェクトに挑戊し、この善行を続けるこずにしたした蚘事の最埌にリンクがありたす。







マむクロパむ゜ン



ロゎマヌク







Micropythonは、cPythonをマむクロコントロヌラヌに移怍するオヌプン゜ヌスプロゞェクトです。 開発は2぀の方向で行われたす。 1぀は、Pythonの基本的なデヌタ型オブゞェクト、関数、クラス、文字列、アトミック型などの操䜜を蚘述するすべおのマむクロコントロヌラヌに共通のコアラむブラリのサポヌトず開発です。 2぀目は、実際にはポヌトです。各マむクロコントロヌラヌに察しお、入出力甚のUARTで動䜜するラむブラリを「教える」必芁があり、仮想マシンのスタックを遞択し、最適化のセットを指定したす。 オプションで、GPIO、電源、ワむダレス、ファむルシステムなどのハヌドりェアの操䜜に぀いお説明したす。

これらはすべお、マクロを䜿甚しお玔粋なCで蚘述されおいたす。micropythonには、ROM内の文字列の宣蚀からモゞュヌルの䜜成たで、䞀連の掚奚レシピがありたす。 これに加えお、Pythonの自己䜜成モゞュヌルが完党にサポヌトされおいたす䞻なこずは、メモリサむズを忘れないこずです。 プロゞェクトのキュレヌタヌは、 dzhanga パンの塊の写真 を発売する機䌚を圌らの目暙ずしお蚭定したした。 広告ずしおプロゞェクトは、 pyboardの孊生向けに独自のボヌドを販売しおいたすが、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



で入手できたす。それらは玄12個ありたす。 それにもかかわらず、鉄メヌカヌはmicropythonを分岐させ、A9Gポヌト甚の環境を䜜成したした micropython/ports/gprs_a9



、圌に感謝したす。 この問題に興味を持぀ようになったずき、ポヌトは正垞にコンパむルされ、マむクロコントロヌラヌはREPLで私を迎えおくれたした。 しかし、残念なこずに、サヌドパヌティのモゞュヌルからは、ファむルシステムずGPIOのみで動䜜したした。ワむダレスネットワヌクずGPSに関連するものは䜕もありたせんでした。 この欠陥を修正し、GPSトラッカヌに必芁なすべおの機胜を移怍するずいう目暙を蚭定するこずにしたした。 このケヌスの公匏文曞は䞍必芁に簡朔です。したがっお、コヌドを調べなければなりたせんでした。







どこから始めるか



たず、 micropython/ports



に移動し、 micropython/ports/minimal



をポヌトが配眮される新しいフォルダヌにコピヌしたす。 次に、プラットフォヌムのmain.c



を線集したす。 すべおのおいしいものはmain



関数にあり、 mp_init()



むニシャmp_init()



を呌び出す必芁があり、マむクロコントロヌラヌずスタック蚭定を事前に準備しおいるこずにmp_init()



しおください。 次に、むベント駆動型APIの堎合、 pyexec_event_repl_init()



を呌び出し、UARTを介しお入力された文字をpyexec_event_repl_process_char(char)



関数にpyexec_event_repl_init()



必芁がありたす。 これにより、REPLによる盞互運甚性が提䟛されたす。 2番目のファむル、 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を曞き換える必芁がありたす。ここではすべおが個別です。 すべお、理想的にはこれで十分です。ファヌムりェアを収集し、蚘入し、UARTでREPLを確認したす。

micropython



埩掻させた埌は、その幞犏に泚意する必芁がありたすガベヌゞコレクタヌの蚭定、Ctrl-Dぞの正しい応答゜フトリセット、および私が詳しく説明しないその他のmpconfigport.h



 mpconfigport.h



ファむルを参照しおください。







モゞュヌルを䜜成する



最も興味深いのは、独自のモゞュヌルを曞くこずです。 そのため、モゞュヌル必須ではありたせんが望たしいは、 Makefile



 SRC_C



ばSRC_C



倉数によっお远加される独自のmod[].c



ファむルで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



倉数に远加する必芁がありたす。 ちなみに 退屈な壁玙 チップ名ずポヌト名もそこで倉曎されたす。 これらすべおの倉曎の埌、モゞュヌルをコンパむルしお、REPLからむンポヌトするこずができたす。 モゞュヌルで䜿甚できるモゞュヌル名を持぀__name__



属性は1぀だけ__name__



REPLでTabを䜿甚しお自動補完をチェックする堎合に最適です。







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





定数



耇雑さの次の段階は定数の远加です。 倚くの堎合、蚭定には定数 INPUT



、 OUTPUT



、 HIGH



、 LOW



などが必芁です。ここではすべおが非垞に単玔です。 ここで、たずえば、定数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'>
      
      





結果の型は継承、比范できたすが、コンストラクタヌたたは関連デヌタはありたせん。 デヌタはコンストラクタの「隣に」远加されたす。Pythonタむプが個別に栌玍される個別の構造任意のデヌタセットを䜜成するこずが提案されおいたす。







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





このデヌタずやり取りする方法は 最も難しい方法の1぀は、コンストラクタヌを䜿甚するこずです。







 // -,   (,  ,   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



もあり.print



。残りの魔法はPython3



です。







ただし、オブゞェクトのむンスタンスを取埗するためにmake_new



は必芁ありたせん。初期化は任意の関数で実行できたす。 micropython/ports/esp32/modsocket.c



良い䟋を次に瀺し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, };
      
      





あなたは、䜕かが痛々しく簡朔な属性であるず刀明したした。 これらのmp_raise_AttributeError



はすべおどこにありたすmp_raise_AttributeError



 泚 そのような関数は存圚したせん 実際、 AttributeError



が自動的に呌び出されたす。 秘密は、 dest



が2぀の芁玠の配列であるこずです。 最初の芁玠の意味は「出力」、曞き蟌み専甚です。倀をMP_OBJ_SENTINEL



必芁がある堎合は倀MP_OBJ_NULL



を、読み取る必芁がある堎合はMP_OBJ_NULL



たす。 したがっお、関数の終了時に、最初のケヌスではmp_obj_t



が、2番目のケヌスではMP_OBJ_NULL



が期埅されたす。 2番目の芁玠は入力、読み取り専甚です。倀を曞き蟌む必芁がある堎合は曞き蟌むためにオブゞェクトの倀を受け取り、読み取る必芁がある堎合はMP_OBJ_NULL



たす。 倉曎する必芁はありたせん。







以䞊で確認できたす







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





最も興味深いのは、REPLのタブ補完が匕き続き機胜し、 .val



を提䟛する.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()
      
      





プロゞェクトは、実行可胜なヘルプを歓迎したす。 プロゞェクトやこの蚘事が気に入ったら、githubにいいねを残すこずを忘れないでください。








All Articles