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
Have questions or comments? Continue the conversation on TechForum, Digi-Key's online community and technical resource.
Visit TechForum