MangOH Red

产品描述

mangOH™ Red 是一个低功耗物联网 (IoT) 平台,采用电池供电,运行寿命长达 10 年。根据 mangoh.io 提供的信息,其特性如下:

  • 信用卡大小,特别适合用于快速构建概念验证;
  • 咬接式插座可以添加任何 CF3™ 兼容模块,包括无线模块(2G 至 4G 和 LTE-M/NB-IoT),以实现长达 10 年的电池寿命(本文使用的 CF3 模块为 WP8548_1103113,DigiKey 零件编号 1645-1022-1-ND);
  • 物联网扩展卡插槽可接入任何基于物联网扩展卡开放标准的技术;
  • 一块集成 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 有两个微型 USB 端口。

    硬件设置很简单:将微型 USB 插入 mangOH 和计算机中,连接天线,并将 SIM 卡插入 mangOH 背面的插槽中。下图是来自 mangOH Red 开发者指南中的硬件架构示意图:

    (图片来源:Talon Communications Inc.)

    Raspberry Pi 连接器

    带状电缆连接器的 DigiKey 零件编号为 1597-1065-ND。下图是 Raspberry Pi 和 MangOH Red 的引脚分配图(Raspberry Pi 引脚分配图中未被 MangOH Red 使用的引脚用阴影隐去):连接方式为引脚 1 接引脚 1,引脚 2 接引脚 2,以此类推。

    (图片来源:pinout.xyz)

    (图片来源:Talon Communications Inc.)

    软件设置

    软件设置稍微复杂一些。当开始使用该器件时,值得注意的是,mangOH 的最终产品中涉及三个实体。MangOH Red 是电路板本身;Sierra Wireless 是 CF3 模块的制造商,也是 AirVantage 的所有者,AirVantage 是存储 mangOH 数据的物联网云;Legato 是基于 Linux 的命令行界面 (CLI),用来连接 mangOH。在接下来的部分中,我将讨论其中每一个实体,以及如何访问其论坛。

    第一个要访问的网站是 mangOH 入门网站。您可以选择 Windows 或 Linux 操作系统(Mac OS 也兼容,稍后我会讨论)。无论选择何种操作系统,您仍然需要通过 Windows 上的虚拟机 (Oracle VirtualBox) 或 Linux 上的 Legato 开发插件来纳入 Legato CLI。Windows 安装手册Linux 安装手册是设置计算机时可使用的极佳资源,因此我不会在此处赘述。

    如果您使用的是 64 位 Linux 计算机,请注意 Linux 安装手册第 16 页的第 6 项。

    您还应下载为 Windows、Linux 和 Mac 操作系统提供的 Developer Studio。据 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 文件夹。对于您从 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

Curtis Johnson 是 DigiKey 的应用工程助理技术员,自 2017 年以来一直在帮助客户解决技术问题,在银行从业 15 年后于 2015 年加入 DigiKey。Curtis 拥有电子技术自动化系统的 AAS、心理学学士学位(我知道与银行或电子学无关)、偏重会计学的 MBA,并完成了经济学和金融学的研究生课程,预期还将在 2018 秋季注册参加计算机科学学士学位计划。Curtis 一直在嵌入式系统、射频、蜂窝通信、天线等领域深入钻研。闲暇时间 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