MangOH Red

產品說明

mangOH™ Red 是一款低功率物聯網 (IoT) 平台,能以電池電力運作長達十年。根據 mangoh.io,此平台具有以下特性:

  • 信用卡大小的外型尺寸,非常適合快速進行概念驗證;
  • 具有扣入式插槽,可加入任何 CF3™ 相容模組,包括無線模組 (2G 至 4G 及 LTE-M/NB-IoT),達到長達 10 年的電池續航力 (本文使用的 CF3 模組為 WP8548_1103113,DigiKey 零件編號 1645-1022-1-ND);
  • 具有 IoT 擴充卡插槽,可加入任何採用 IoT 擴充卡開放標準的技術;
  • 整合式 Sierra Wireless 智慧 SIM,隨附多達 100 MB 的免費數據流量 (視區域而定),並可搭配任何市售 SIM 使用;
  • 整合 AirVantage IoT 平台,可在雲端建構、部署並管理解決方案;
  • 內建 Wi-Fi b/g/n 和藍牙 4.2 BLE,並搭載 Cortex-M4,可即時存取 I/O;
  • 內建加速計/陀螺儀、壓力和光感測器,並具有 26 引腳 Raspberry Pi 相容連接器 (連接器纜線的 DigiKey 零件編號為 1597-1065-ND)。

    (圖片來源:Talon Communications Inc.)

    包裝內容物

    開箱內容物:

    • mangOH Red
    • CF3 模組 (此例為 WP8548_1103113)
    • Micro-USB 纜線
    • 2 個天線 (1 個主天線及 1 個全球導航衛星系統 (GNSS))
    • Sierra Wireless micro-SIM 卡 (配有初始數據流量)

    硬體安裝

    依照 mangOH Red 使用與安裝指南的安裝指示操作。

    MangOH Red 有兩個 micro-USB 連接埠。

    硬體安裝相當直覺,只要將 micro-USB 插入 mangOH 與電腦,接上天線,再將 SIM 卡插入 mangOH 背面的插槽即可。下圖顯示取自 mangOH Red 開發人員指南的硬體架構圖:

    (圖片來源:Talon Communications Inc.)

    Raspberry Pi 連接器

    帶狀纜線連接器的 DigiKey 零件編號為 1597-1065-ND。下圖顯示 Raspberry Pi 與 MangOH Red 的針腳配置圖 (Rasperry Pi 的針腳配置圖中,MangOH Red 未使用的引腳已被塗掉):連接方式為針腳 1 對針腳 1、針腳 2 對針腳 2,依此類推。

    (圖片來源:pinout.xyz)

    (圖片來源:Talon Communications Inc.)

    軟體安裝

    軟體安裝則稍微複雜一點。開始使用此裝置時,應注意到 mangOH 的最終成品牽涉三大廠商。MangOH Red 就是板件本身;Sierra Wireless 是 CF3 模組的製造商也是 AirVantage 的所有者。AirVantage 屬於 IoT 雲端服務,可儲存來自 mangOH 的資料;Legato 是 Linux 架構的命令列介面 (CLI),可用來與 mangOH 連線。我會在過程中一一討論這些部分,並說明如何連結到這些廠商的論壇。

    第一個要造訪的網站是開始使用 mangOH 網站。您可以選擇使用 Windows 或 Linux 作業系統 (Mac OS 也相容,我稍候會談到)。無論您選用何種作業系統,您仍然要透過 Windows 上的虛擬機 (Oracle VirtualBox) 或 Linux 上的 Legato 開發附加元件來結合 Legato CLI。Windows 安裝手冊Linux 安裝手冊是設置機器時很棒的資源,此處不再重述。

    如果您使用 64 位元的 Linux 機器,請注意 Linux 安裝手冊第 16 頁第 6 項。

    您也應該下載 Developer Studio (有提供 Windows 版、Linux 版與 Mac 版)。根據 Sierra Wireless 網站:「Developer Studio 是 Legato 應用程式框架所使用的 Sierra Wireless 整合式開發環境 (IDE)」。這代表您有兩種方式能建立自己的應用程式:使用基本的文字程式,或使用 Developer Studio (圖像式程式,且會自動化處理部分檔案/資料夾的建立作業)。我在每個作業系統都下載 Developer Studio,只遇到一個小問題,就是 Mac OS 的 Java 8 需求;Java 8 的一般安裝版本不足以運行 Developer Studio,因此我最後下載並安裝了 Oracle 的 Java SE 開發套件 8u151

    範例應用程式與檔案結構

    安裝好 mangOH 後,最好從 Github 上的 Legato 開始。下載或複製 Legato 資料夾。您不妨造訪 Legato 範例應用程式說明,進一步瞭解您在 Github 所下載的一些範例。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

Curtis Johnson 是 DigiKey 的應用工程技佐,自 2017 年起開始協助客戶解決技術問題。他在結束 15 年的銀行員生涯後於 2015 年加入 DigiKey 團隊。Curtis 擁有電子技術自動化系統的副學士學位、心理學學士學位 (我知道,這跟銀行或電子都沒關係) 以及專精會計的商管碩士學位,更完成經濟與財經的學士後課程,更將在 2018 年秋季註冊就讀電腦科學的碩士學位課程。Curtis 不斷努力深入瞭解嵌入式系統、RF、蜂巢通訊等領域。他喜歡在閒暇時間陪同家人、閱讀與學習。

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