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

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