mangOH Red

製品概要

mangOH™ Redは、低消費電力のモノのインターネット(IoT)プラットフォームであり、バッテリ電源で最大10年間動作します。mangoh.ioでは、次のように説明されています。

  • クレジットカードサイズのフォームファクタで、概念実証の迅速な構築に理想的。
  • スナップインソケットにより、任意のCF3™互換モジュールの追加が可能。ワイヤレスモジュール(2G~4GおよびLTE-M/NB-IoT)を使用すると最大10年間のバッテリ駆動が可能(この記事で使用したCF3モジュールは、WP8548_1103113(DigiKey品番1645-1022-1-ND)でした)。
  • IoT拡張カードスロットには、IoT拡張カードのオープン標準に基づくあらゆるテクノロジのプラグインが可能。
  • 内蔵のSierra WirelessスマートSIMに最大100MB(リージョンによる)のフリーデータを保持でき、市販のあらゆるSIMと併用が可能。
  • AirVantage IoTプラットフォームと統合されており、クラウドでのソリューションの作成、展開、管理が可能。
  • 内蔵のWi-Fi b/g/nとBluetooth 4.2 BLEおよびCortex-M4により、I/Oへのリアルタイムアクセスが可能。
  • 加速度センサ/ジャイロスコープ、圧力および光センサを内蔵、26ピンRaspberry Pi対応コネクタ(このコネクタケーブルは、DigiKey品番1597-1065-NDです)。

    (画像提供:Talon Communications Inc.

    同梱されていたもの

    箱の中には、以下のアイテムが入っていました。

    • mangOH Red
    • CF3モジュール(我々のは、WP8548_1103113)
    • マイクロUSBケーブル
    • アンテナ × 2(1本はメイン用、もう1本は全地球航法衛星システム(GNSS)用)
    • 初期データの割り当て済みのSierra WirelessマイクロSIMカード

    ハードウェアのセットアップ

    セットアップ手順については、mangOH Redユーザーおよびセットアップガイドに従ってください。

    mangOH Redには、2つのマイクロUSBポートがあります。

    ハードウェアのセットアップは簡単です。マイクロUSBをmangOHに繋いでコンピュータに接続します。アンテナを接続し、SIMカードをmangOHの背面スロットに差し込みます。次の図は、mangOH Red開発者ガイドから抜粋したハードウェアアーキテクチャ図です。

    (画像提供:Talon Communications Inc.)

    Raspberry Piコネクタ

    リボンケーブルコネクタのDigiKey品番は1597-1065-NDです。下の画像は、Raspberry Pi(Raspberry Piのピン配置図では、MangOH Redが使用しないピンは網掛けされています)とMangOH Redのピン配置図を示しています。接続は、ピン1〜ピン1、ピン2〜ピン2、以下同様のようになります。

    (画像提供:pinout.xyz)

    (画像提供:Talon Communications Inc.)

    ソフトウェアのセットアップ

    ソフトウェアのセットアップは少し面倒でした。このデバイスを使って作業を始めるときは、mangOHの最終製品には3つのエンティティが絡んでいることに十分注意してください。mangOH Redはボード本体です。Sierra Wirelessは、CF3モジュールのメーカーであると同時に、mangOHからのデータを格納するIoT CloudであるAirVantageの所有者でもあります。また、Legatoは、Linuxベースのコマンドラインインターフェース(CLI)ですが、mangOHとの接続に使用されます。これらそれぞれについてと、そのフォーラムへのアクセスの仕方をこれから順に説明します。

    最初に閲覧するウェブサイトは、Getting Started with mangOH(mangOHのスタートアップ)のページです。ここでは、WindowsかLinuxのどちらかのオペレーティングシステムを選択できます(Mac OSも同様ですが、後述します)。どちらのOSを選んでも、Windows上の仮想マシン(Oracle VirtualBox)またはLinux上のLegato開発用アドオンのいずれかを使用して、Legato CLIを組み込む必要があります。Windows用セットアップマニュアルLinux用セットアップマニュアルは、マシンのセットアップ時の重要な情報であり、ここではその内容は繰り返しません。

    64ビットのLinuxマシンを使用している場合は、Linuxセットアップマニュアルの16ページの項目6に注意してください。

    Windows、Linux、Mac OSで使用できるDeveloper Studioもダウンロードする必要があります。Sierra Wirelessのウェブサイトによると、「Developer Studioは、Legato Application Framework用のSierra Wireless IDE(統合開発環境)」ということです。つまり、自分でアプリを作成するには2つの選択肢があります。ベーシックなテキストベースのプログラムを使用するか、それともGUIベースのDeveloper Studioを使用して、ファイル/フォルダ作成の一部をオートで行うかです。私は各OS用のDeveloper Studioをダウンロードしてみましたが、唯一問題があったのはMac OS用のJava 8でした。Java 8の標準インストールでは、何らかの理由でDeveloper Studioが動作しなかったので、OracleのサイトからJava SE Development Kit 8u151をダウンロードし、これをインストールしました。

    サンプルアプリとファイル構造

    mangOHをセットアップしたら、手始めに行くべきは、Legato on Githubのサイトです。ここでLegatoフォルダをダウンロードするか、クローンを作成します。Githubでダウンロードしたサンプルについては、Legatoのサンプルアプリの説明を参照してください。Legatoサンプルアプリのウェブサイトは、素晴らしい「ノウハウ」の宝庫です。

    ファイル構造にも注意を払ってください。各アプリケーションには、次のようなディレクトリ/ファイル構造があります。

    アプリケーションによっては、ファイル構造には.APIファイルや.hファイルも含まれています。プログラミングに関しては、コアは.cファイルで、他のファイルはアプリケーション作成のためのコンパイル手順です。

    例:GNSS位置テキストアプリ

    mangOH Redに何ができるかをよく示しているサンプルアプリケーションは、mangOH Redの連動機能をユーザーにテキストで表示するアプリです。ファイル構造は上記のとおりです。

    textLoc.cファイルのコードは以下のとおりです。Randall Restle氏の新製品発見をチェックするには、ここをクリックしてください。ここでは、同氏が初めてmangOH Redを紹介しています。

    コピー
    textLoc.c
        //--------------------------------------------------------------------------------------------------
    /** @file textLoc.c
    *
    * This app illustrates a sample usage of ultra low power mode API. This app reads the current gps
    * location and then sends it as a text message to a destination cell phone number. Once the text
    * message has been sent, the device enters ultra low power mode. The device will wake up from
    * ultra low power mode after a configurable delay.
    *
    * @note This app expects destination cell number to be specified in environment variable section
    * of adef file. If nothing specified in environment variable, it will send message to a default
    * non-existent phone number.
    *
    * Copyright (C) Sierra Wireless Inc.
    */
    //--------------------------------------------------------------------------------------------------
    
    #include "legato.h"
    /* IPC APIs */
    #include "interfaces.h"
    
    //--------------------------------------------------------------------------------------------------
    /**
    * GPS timeout interval in minutes
    *
    * @note Please change this timeout value as needed.
    */
    //--------------------------------------------------------------------------------------------------
    #define GPSTIMEOUT 15
    
    //--------------------------------------------------------------------------------------------------
    /**
    * Default phone number to send location information.
    *
    * @note This is a non-existent phone number (ref:
    * https://en.wikipedia.org/wiki/Fictitious_telephone_number).
    */
    //--------------------------------------------------------------------------------------------------
    #define DEFAULT_PHONE_NO "8005550101"
    
    //--------------------------------------------------------------------------------------------------
    /**
    * Timer interval(in seconds) to exit from shutdown/ultralow-power state.
    *
    * @note Please change this interval as needed.
    */
    //--------------------------------------------------------------------------------------------------
    #define ULPM_EXIT_INTERVAL 30
    
    //--------------------------------------------------------------------------------------------------
    /**
    * Gpio used to exit from shutdown/ultralow-power state.
    *
    * @note Please change gpio number as needed.
    */
    //--------------------------------------------------------------------------------------------------
    #define WAKEUP_GPIO_NUM 38
    
    //--------------------------------------------------------------------------------------------------
    /**
    * Pointer to the null terminated string containing the destination phone number.
    */
    //--------------------------------------------------------------------------------------------------
    static const char *DestPhoneNumberPtr;
    
    //--------------------------------------------------------------------------------------------------
    /**
    * Attempts to use the GPS to find the current latitude, longitude and horizontal accuracy within
    * the given timeout constraints.
    *
    * @return
    * - LE_OK on success
    * - LE_UNAVAILABLE if positioning services are unavailable
    * - LE_TIMEOUT if the timeout expires before successfully acquiring the location
    *
    * @note
    * Blocks until the location has been identified or the timeout has occurred.
    */
    //--------------------------------------------------------------------------------------------------
    static le_result_t GetCurrentLocation(
    int32_t *latitudePtr, ///< [OUT] latitude of device - set to NULL if not needed
    int32_t *longitudePtr, ///< [OUT] longitude of device - set to NULL if not needed
    int32_t *horizontalAccuracyPtr, ///< [OUT] horizontal accuracy of device - set to NULL if not
    ///< needed
    uint32_t timeoutInSeconds ///< [IN] duration to attempt to acquire location data before
    ///< giving up. A value of 0 means there is no timeout.
    )
    {
    le_posCtrl_ActivationRef_t posCtrlRef = le_posCtrl_Request();
    if (!posCtrlRef)
    {
    LE_ERROR("Can't activate the Positioning service");
    return LE_UNAVAILABLE;
    }
    
    le_result_t result;
    const time_t startTime = time(NULL);
    LE_INFO("Checking GPS position");
    while (true)
    {
    result = le_pos_Get2DLocation(latitudePtr, longitudePtr, horizontalAccuracyPtr);
    if (result == LE_OK)
    {
    break;
    }
    else if (
    (timeoutInSeconds != 0) &&
    (difftime(time(NULL), startTime) > (double)timeoutInSeconds))
    {
    result = LE_TIMEOUT;
    break;
    }
    else
    {
    // Sleep for one second before requesting the location again.
    sleep(1);
    }
    }
    
    le_posCtrl_Release(posCtrlRef);
    
    return result;
    }
    
    //--------------------------------------------------------------------------------------------------
    /**
    * Sends an SMS text message to the given destination with the given message content.
    *
    * @return
    * - LE_OK on success
    * - LE_FAULT on failure
    */
    //--------------------------------------------------------------------------------------------------
    static le_result_t SendTextMessage(
    const char *destinationNumberPtr, ///< [IN] Phone number to send the text message to as ASCII
    ///< values
    const char *messageBodyPtr ///< [IN] Text message body content
    )
    {
    le_result_t result = LE_OK;
    LE_INFO("Sending SMS");
    
    le_sms_MsgRef_t sms = le_sms_Create();
    
    if (le_sms_SetDestination(sms, destinationNumberPtr) != LE_OK)
    {
    result = LE_FAULT;
    LE_ERROR("Could not set destination phone number");
    goto sms_done;
    }
    
    if (le_sms_SetText(sms, messageBodyPtr) != LE_OK)
    {
    result = LE_FAULT;
    LE_ERROR("Could not set text message body");
    goto sms_done;
    }
    
    if (le_sms_Send(sms) != LE_OK)
    {
    result = LE_FAULT;
    LE_ERROR("Could not send SMS message");
    goto sms_done;
    }
    
    LE_INFO("SMS Message sent");
    
    sms_done:
    le_sms_Delete(sms);
    return result;
    }
    
    //--------------------------------------------------------------------------------------------------
    /**
    * Send the device location as a text message
    *
    * Attempts to send an SMS text message containing the current device location to the destination
    * phone number.
    *
    * @note
    * No failure notification is provided if location services or SMS send are unsuccessful.
    */
    //--------------------------------------------------------------------------------------------------
    static void SendSmsCurrentLocation(
    void
    )
    {
    char smsBody[LE_SMS_TEXT_MAX_LEN];
    int32_t latitude;
    int32_t longitude;
    int32_t horizontalAccuracy;
    
    const le_result_t result =
    GetCurrentLocation(&latitude, &longitude, &horizontalAccuracy, GPSTIMEOUT * 60);
    if (result == LE_OK)
    {
    snprintf(smsBody, sizeof(smsBody), "Loc:%d,%d", latitude, longitude);
    }
    else
    {
    strncpy(smsBody, "Loc:unknown", sizeof(smsBody));
    }
    SendTextMessage(DestPhoneNumberPtr, smsBody);
    }
    
    //--------------------------------------------------------------------------------------------------
    /**
    * Configure the boot source and shutdown MDM.
    */
    //--------------------------------------------------------------------------------------------------
    static void CfgShutDown
    (
    void
    )
    {
    // Boot after specified interval.
    if (le_ulpm_BootOnTimer(ULPM_EXIT_INTERVAL) != LE_OK)
    {
    LE_ERROR("Can't set timer as boot source");
    return;
    }
    
    // Boot on gpio. Please note this is platform dependent, change it when needed.
    if (le_ulpm_BootOnGpio(WAKEUP_GPIO_NUM, LE_ULPM_GPIO_LOW) != LE_OK)
    {
    LE_ERROR("Can't set gpio: %d as boot source", WAKEUP_GPIO_NUM);
    return;
    }
    
    // Initiate shutdown.
    if (le_ulpm_ShutDown() != LE_OK)
    {
    LE_ERROR("Can't initiate shutdown.");
    }
    }
    
    //--------------------------------------------------------------------------------------------------
    /**
    * Callback function to handle change of network registration state.
    */
    //--------------------------------------------------------------------------------------------------
    static void RegistrationStateHandler
    (
    le_mrc_NetRegState_t state, ///< [IN] Network registration state.
    void *contextPtr ///< [IN] Context pointer.
    )
    {
    switch(state)
    {
    case LE_MRC_REG_HOME:
    case LE_MRC_REG_ROAMING:
    LE_INFO("Registered");
    SendSmsCurrentLocation();
    LE_INFO("Now configure boot source and shutdown MDM");
    CfgShutDown();
    break;
    case LE_MRC_REG_SEARCHING:
    LE_INFO("Searching...");
    break;
    case LE_MRC_REG_NONE:
    LE_INFO("Not registered");
    break;
    case LE_MRC_REG_DENIED:
    LE_ERROR("Registration denied");
    break;
    case LE_MRC_REG_UNKNOWN:
    LE_ERROR("Unknown registration state");
    break;
    }
    }
    
    //--------------------------------------------------------------------------------------------------
    /**
    * Simulate entry into the current NetReg state by calling RegistrationStateHandler
    *
    * RegistrationStateHandler will only be notified of state change events. This function exists to
    * simulate the change into the current state.
    */
    //--------------------------------------------------------------------------------------------------
    static void SimulateNetRegStateChangeToCurrentState(
    void *ignoredParam1, ///< Only exists to allow this function to conform to the
    ///< le_event_DeferredFunc_t declaration
    void *ignoredParam2 ///< Only exists to allow this function to conform to the
    ///< le_event_DeferredFunc_t declaration
    )
    {
    le_mrc_NetRegState_t currentNetRegState;
    LE_FATAL_IF(le_mrc_GetNetRegState(¤tNetRegState) != LE_OK, "Couldn't get NetRegState");
    RegistrationStateHandler(currentNetRegState, NULL);
    }
    
    //--------------------------------------------------------------------------------------------------
    /**
    * Get the destination phone number.
    **/
    //--------------------------------------------------------------------------------------------------
    static void GetDestinationCellNo
    (
    void
    )
    {
    DestPhoneNumberPtr = getenv("DEST_CELL_NO");
    if (!DestPhoneNumberPtr)
    {
    LE_WARN(
    "No destination cell number is specified. Using a default non-existent number");
    DestPhoneNumberPtr = DEFAULT_PHONE_NO;
    }
    
    LE_INFO("Destination phone number = %s", DestPhoneNumberPtr);
    }
    
    
    COMPONENT_INIT
    {
    char version[LE_ULPM_MAX_VERS_LEN + 1];
    
    LE_INFO("TextLoc started");
    
    // Get ultra low power manager firmware version
    LE_FATAL_IF(
    le_ulpm_GetFirmwareVersion(version, sizeof(version)) != LE_OK,
    "Failed to get ultra low power firmware version");
    LE_INFO("Ultra Low Power Manager Firmware version: %s", version);
    
    // Now check whether boot was due to timer expiry.
    if (le_bootReason_WasTimer())
    {
    LE_INFO("Booted from timer, not sending another text message.");
    }
    else if (le_bootReason_WasGpio(WAKEUP_GPIO_NUM))
    {
    LE_INFO("Booted from GPIO, not sending another text message.");
    }
    else
    {
    // Get the destination phone number
    GetDestinationCellNo();
    
    // Register a callback handler for network registration state.
    le_mrc_AddNetRegStateEventHandler(
    (le_mrc_NetRegStateHandlerFunc_t)RegistrationStateHandler, NULL);
    le_event_QueueFunction(&SimulateNetRegStateChangeToCurrentState, NULL, NULL);
    }
    }
    The textLoc.adef file is:
    textLoc.adef  
    sandboxed: false
    
    version: 1.0.0
    maxFileSystemBytes: 512K
    
    executables:
    {
    textloc = ( textLocComponent )
    }
    
    processes:
    {
    envVars:
    {
    LE_LOG_LEVEL = DEBUG
    // This is a non-existent fictitious phone number. Change it to a valid phone number.
    DEST_CELL_NO = 8005550101
    }
    run:
    {
    ( textloc )
    }
    maxCoreDumpFileBytes: 512K
    maxFileBytes: 512K
    }
    
    bindings:
    {
    textloc.textLocComponent.le_mrc -> modemService.le_mrc
    textloc.textLocComponent.le_posCtrl -> positioningService.le_posCtrl
    textloc.textLocComponent.le_pos -> positioningService.le_pos
    textloc.textLocComponent.le_sms -> modemService.le_sms
    textloc.textLocComponent.le_ulpm -> powerMgr.le_ulpm
    textloc.textLocComponent.le_bootReason -> powerMgr.le_bootReason
    }
    
    The Component.cdef:
    Component.cdef  
    requires:
    {
    api:
    {
    modemServices/le_mrc.api
    modemServices/le_sms.api
    positioning/le_posCtrl.api
    positioning/le_pos.api
    le_ulpm.api
    le_bootReason.api
    }
    }
    
    sources:
    {
    textLoc.c
    }
    Makefile:
    Makefile  
    TARGETS := $(MAKECMDGOALS)
    
    .PHONY: all $(TARGETS)
    all: $(TARGETS)
    
    $(TARGETS):
    mkapp -v -t $@ \
    textLoc.adef
    
    clean:
    rm -rf _build_* *.update
    

著者について

Image of Curtis Johnson

DigiKeyのアソシエートアプリケーションエンジニアリングテクニシャンであるCurtis Johnsonは、2017年以来、技術的な質問を持つお客様を支援してきました。また15年間、銀行家として勤めた後、2015年以来、DigiKeyで仕事を続けています。Curtisは、Electronics Technology Automated SystemsでのAAS、(銀行やエレクトロニクスとは関係ありませんが)心理学の学士号、会計学に重点を置いたMBA、経済学と金融学での大学院の研究を修了し、2018年秋にはコンピュータサイエンスの学士号プログラムに入学することになっています。Curtisは、組み込みシステム、RF、セルラー通信、その他の分野でより多くの理解を得ることに取り組んでいます。Curtisは自由な時間には、家族、読書、学習に時間を費やしています。

More posts by Curtis Johnson
 TechForum

Have questions or comments? Continue the conversation on TechForum, Digi-Key's online community and technical resource.

Visit TechForum