研究筆記 - LASS on LinkItOne Code Trace
- 2016/7/10 updated
- git rev: 01e0e13
1. 前言
我雖然一直有關注 LASS,但因為工作忙碌,所以一直沒有時間好好研究。直到現在 LASS 已經漸漸發展到一個稍具規模的專案了。這篇文章記錄我在颱風假時邊看邊研究的歷程。主要的重點放在了解程式怎麼運作、以及 "猜測" 背後的設計想法(畢竟工程師很不愛寫文件的 = =)。
1.1. 起手式
資源
LASS 的分工算是很精細的,資源整理都做得很不錯。facebook 就是哈拉討論和活動發布。hackpad 上面整理很多文件、資源,以及正在進行中的專案。基本上 hackpad 看 「LASS - README」就夠了,它是一個不錯的入口頁面。
- Facebook: LASS-開源公益的環境感測器網路
- HackPad: LASS
- GitHub: LinkItONEDevGroup/LASS
因為是 code trace,所以我會把重點放在 github。
1.2. github
LASS 的 github 名稱是 LinkItONEDevGroup,原因是第一版的 LASS,就是用 LinkItOne 做出來的。現在已經漸漸移植到不同種類的開發板了。目前 github (rev:01e0e13) 上面的資料夾如下:
- LASS/
- |-- 3dp # 各個專案的 3D 列印外殼檔,有 .stl 檔,有的甚至有安裝說明
- |-- Companion-module # 其實我不知道這是哪個專案用的,裡面有 SSD1306 OLED 的 lib,
- | 還有一支 python 寫的測試程式
- |-- DataPresentation # 看起來像是把資料呈現在網頁的展示頁
- |
- |-- Device_Ameba # Realtek Ameba 的子專案,看起來有用到 Gemtek LoRa Module
- |-- Device_ArduinoCrossFit # 不知道是什麼東西,但重點似乎是 PM2.5
- |-- Device_ArduinoLike # 看起來像是要移植到 Arduino 的樣子?
- |
- |-- Device_LinkItOne # 老實說 LinkItOne 的主專案還是比較完整
- | |-- Blynk * 可以透過 app UI 控制的東西吧?
- | |-- Derivative * 位置安排的怪怪的,裡面有 "魚菜共生" (Aquaponics)
- | |-- Example * Sensors 的驗證測試程式
- | |-- LASS * 主程式
- | `-- Libraries * 函示庫
- | |-- AWSArduinoMediaTekLibrary.zip
- | |-- Blynk_v0.3.3.zip
- | |-- DHT_linkit.zip
- | |-- Grove_Digital_Light_Sensor.zip
- | |-- HP20x_dev.zip
- | |-- HardwareLibrary.zip
- | |-- PubSubClient.zip
- | `-- arduino-sht.zip
- |
- |-- Device_LinkItSmart7688Duo # Smart7688Duo 的版本,裡面有 python 和 node.js 版本
- |-- Device_Rpi # 目前沒東西 (其實我覺得純用 Pi 做比較辛苦)
- |-- Doc # 專案介紹的投影片
- |
- |-- IASS # 由凌陽創新科技釋出的室內環境控制系統,蠻完整的。
- | | 文件、程式碼在 github 上都整理得蠻好的
- | |-- IASS_BASIC
- | | |-- Devices # 他們訂了三個應用的場域,居家安全、機房、辦公室
- | | |-- Docs # 文件蠻詳細的,主要以 Arduino UNO 為 based
- | | |-- IASS_Server
- | | |-- Sensors_support
- | | `-- arch.gif # 這個看一下
- | `-- README.md
- |
- |-- LASS4U # 室內環境檢測的子計畫,主要增加 CO2 的檢測
- | 其實它應該算是個蠻完整的專案了(都生產了) 但是 github 上面沒什麼資源
- |
- |-- LASS_DB # 用 MongoDB 和 BSON 格式。README.md 看一下
- |-- LICENSE
- |-- QandA.txt
- |-- README.md
- |-- SensingOfRiceFields # 農業感測計畫
- `-- WeatherBox # 農業感測計畫的子計畫: 用來偵測大氣氣候
1.3. github: Device_LinkItOne
我想最完整的概念和實作應該還是在 LinkItOne 的版本中。以這個專案當切入點應該不錯!
- Device_LinkItOne
- |-- Blynk
- |-- Derivative
- |-- Example
- |-- LASS
- `-- Libraries
- |-- AWSArduinoMediaTekLibrary.zip
- |-- Blynk_v0.3.3.zip
- |-- DHT_linkit.zip
- |-- Grove_Digital_Light_Sensor.zip
- |-- HP20x_dev.zip
- |-- HardwareLibrary.zip
- |-- PubSubClient.zip
- `-- arduino-sht.zip
1.4. Device_LinkItOne/Libraries
- Blynk_v0.3.3.zip
- 從 blynk github 可以下載到
- 完全沒更改。
- 官方文件: blynk docs
- DHT_linkit.zip
- 從 SeeedStudio github: Grove_Starter_Kit_For_LinkIt 可以下載到
- 內容幾乎沒更改,只有改名 DHT -> DHT_linkit
- 模組: Grove - Temperature and Humidity Sensor Pro
- Grove_Digital_Light_Sensor.zip
- 從 SeeedStudio github: Grove_Digital_Light_Sensor 可以下載到
- 完全沒更改
- 模組 (I2C): Grove - Digital Light Sensor
- HP20x_dev.zip (高準確性的氣壓計)
- 從 SeeedStudio github: Grove_Barometer_HP20x 可以下載到
- 內容幾乎沒更改,只有拿掉幾個 include (我猜是因為 build 的時候產生 re-defined?)
- 模組: Grove - Barometer (High-Accuracy)
- HardwareLibrary.zip (這命名實在是…其實是三軸加速度感應器: ADXL345)
- 下載點: ADXL345
- Seeed-Studio github: Accelerometer_ADXL345
- 小改一個強制轉型而已
- PubSubClient.zip
- 比較接近 pubsubclient v1.9.1, 2012/11/12 released。
- 有一些修改,commit log 寫: remove “Icon” in the Zip and make it compatiable with MS Windows system
- 修改的部分有:
- unsigned int 改為 uint16_t
- MQTT_KEEPALIVE 從 15 -> 120 (keepAlive interval in Seconds)
- MQTT_MAX_PACKET_SIZE 從 128 -> 768 (Maximum packet size)
- 因為 size 變化所做的調整 (PubSubClient.cpp)
- arduino-sht.zip (支援 SHTC1, SHT3x 濕度感應器)
- 從 winkj’s github 可以下載到
- 完全沒更改
- AWSArduinoMediaTekLibrary.zip (連接到 AWS 的 lib)
- 從 ithub: aws-sdk-arduino 可以下載到
- 套件裡面就是 aws-sdk-arduino/src/common 以及 aws-sdk-arduino/src/mediatek 的綜合體。
- 簡單的修改 AWSClient2.h 變數名稱
- AmazonDynamoDBClient.cpp 中修改了 calss AttributeValue 的成員名稱 (我看不懂,跳過 XD)
.
- aws-sdk-arduino
- |-- LICENSE.txt
- |-- NOTICE.txt
- |-- README.md
- |-- samples
- | |-- EdisonSNSSample.ino
- | |-- Edison_Maraca
- | |-- GalileoSample.ino
- | |-- MediaTek_Maraca_HTTPS
- | |-- SparkGetItemSample.ino
- | |-- SparkPutItemSample.ino
- | `-- SparkSNSSample.ino
- `-- src
- |-- common #<= 在這裡!!
- |-- edison
- |-- galileo
- |-- mediatek #<= 在這裡!!
- `-- sparkcore
1.5. code trace tools
如果要在 Linux 上面 code trace 的話, vim 的 syntax plugin 可以裝。
- 下載 Arduino syntax file 到 ~/.vim/syntax/
- $ vi ~/.vimrc 多這幾行
- " ++++++++++ for arduino +++++++++++++
- autocmd! BufNewFile,BufRead *.ino setlocal ft=arduino
- autocmd! BufNewFile,BufRead *.pde setlocal ft=arduino
- ctags 指令: (重點是 --langmap 那個參數)
- find . -type f -name ".[ch]" -o -name ".cpp" -o -name ".ino" -o -name ".pde" | sed -e ‘s/^/"/’ -e ‘s/$/"/’ | xargs /usr/bin/ctags -a --langmap=c++:.ino
- $ vi LASS/LASS.ino # enjoy your ctags
2. 主程式 LASS.ino
2.1. Code Comment Info
裡面有些資訊,既然它寫很長,表示有詳細的說明先看吧:
Board jumper setting:
- SPI/SD => SPI
- MS/UART => UART
- USB/BAT => USB
Default Output PIN:
- ARDUINO_LED_PIN = 13
- STORAGE_CHIP_SELECT_PIN = 10 (Reserved)
Sensor Input PIN:
- SOUND_SENSOR_PIN = A1
- DUST_SENSOR_PIN = 8, cal
- UV_SENSOR_PIN = A0
- TEMP-HUMID_SENSOR_PIN = 2
- BAROMETER_SENSOR_PIN -> I2C
- LIGHT_SENSOR -> I2C
Action Output PIN:
Sensor position definition:
- 0-9 (System sensor)
- reserved for system sensor, should not be modified for individual application purpose.
- 10-19 (User sensor)
- user customization purpose, user can freely customize this area
Note: 這些註解現在還是這樣嗎?
Default system sensor order:
- 0: record_id
- 1: battery level
- 2: battery charging
- 3: ground speed ( Km/hour )
Default user sensor order:
- 10: dust sensor
- 11: UV sensor
- 12: sound sensor
Note: 這些註解現在還是這樣嗎?
Default user sensor order in APP-3 (MAPS):
- 10: barometer sensor (high accuracy)
- 11&12: temperature & humidity sensor pro
- 13: digital light sensor
Note: 這些註解現在還是這樣嗎?
Default Sensor Type:
- -: void; 0-9: reserved for system sensor
- A-Z: LASS default sensors
- a-z: reserved for other sensors
Note: 這些註解現在還是這樣嗎?
- -: unused
- 0: record ID
- 1: battery level
- 2: battery charging
- 3: ground speed
- 4: wifi debug: reconnect count
- d: dust sensor
- u: UV sensor
- s: sound sensor
- b: barometer sensor (hPa)
- t: temperature sensor (degree C)
- h: humidity sensor (%)
- l: light sensor (LUX)
Optional sample sensor: (幾乎都是 seeedstudio 的 Grove 模組)
- AP1-SENSOR_ID_DUST: Dust sensor
- AP1-SENSOR_ID_UV: UV sensor
- AP1-SENSOR_ID_SOUND: Sound sensor
- AP2-SENSOR_ID_DUST: Dust sensor (G3)
- AP3-SENSOR_ID_BAROMETER: Barometer sensor (high accuracy)
- AP3-SENSOR_ID_TEMPERATURE: Temperture and Humidity sensor pro
- AP3-SENSOR_ID_HUMIDITY: Temperture and Humidity sensor pro
- AP3-SENSOR_ID_LIGHT: Digital Light sensor
- AP4-SENSOR_ID_DUST: Dust sensor
- AP4-SENSOR_ID_TEMPERATURE: Temperture and Humidity sensor pro
- AP4-SENSOR_ID_HUMIDITY: Temperture and Humidity sensor pro
Note: 其實我不知道那個 AP1,..AP4 代表什麼意思
Optional alarm:
2.2. configuration.h
第一步,先設定 WiFi SSID & PASSWD,預設使用 WPA
- //LASS CONFIGURATION FILE
- #include "Arduino.h"
- //Step 1: How you Connect WIFI....Basic things.
- //WIFI
- //System default wifi setting: SSID=LASS, PASS=LASS123456, WIFI_AUTH=LWIFI_WPA
- #define WIFI_SSID "LASS" // REPLACE: your network SSID (name)
- #define WIFI_PASS "LASS123456" // REPLACE: your network password (use for WPA, or use as key for WEP)
- #define WIFI_AUTH LWIFI_WPA //Default:LWIFI_WPA // choose from LWIFI_OPEN, LWIFI_WPA, or LWIFI_WEP.
- //-----------------------------------------------------------
是否使用 Blynk。如果要使用還需要填入 blynk_auth ID
- //Step 2:Do you use Blyak.If yes,fill info below.
- //Blynk-IoT
- #define BLYNK_ENABLE 0 // deafult(0) 0: If you don’t need to support BLYNK, 1: support BLYNK
- char blynk_auth[] = "yourBlynkID"; // REPLACE: your Blynk auto id
- //-----------------------------------------------------------
設定 MQTT,主要是設定 DEVICE_ID
- //Step 3:MQTT info
- //MQTT-IoT
- #define MQTT_PROXY_IP "gpssensor.ddns.net" // Current LASD server , dont change!
- #define DEVICE_TYPE "LinkItONE" // since there is only one device LASS supported now,dont change!
- #define DEVICE_ID "FT1_999" // REPLACE: The device ID you like, please start from LASD. Without this prefix, maybe it will be filter out.
- #define MQTT_TOPIC_PREFIX "LASS/Test" // CAN REPLACE if you like //Dont Replace IF YOU ARE FIELD-TRY USER
- #define PARTNER_ID "LASS-Partner1" // CAN REPLACE if you like
設定 GPS。設定 FAKE_GPS=1 可以使用假的 GPS 位置傳資料,位置定義於 gps_lat, gps_lon, gps_alt。
GPS_SIGNAL_NOCHECK: 我猜是要不要檢查 GPS 訊號正常才傳送?
- //Step 4:GPS
- //Do you want to use gps? 0:YES 1:FAKE GPS
- #define FAKE_GPS 0 // FAKE_GPS : 0: default format with gps, 1: default format but gps is fix data, need to update GPS_FIX_INFOR
- //NOTICE:If you choose 1 modify "FAKE" GPS location. Fill info below
- const char gps_lat[]= "23.000000"; // device’s gps latitude
- const char gps_lon[]= "120.000000"; // device’s gps longitude
- const char gps_alt[]= "30.0"; // device’s gps altitude
- #define GPS_SIGNAL_NOCHECK 1 // 0: log or send only when GPS have signal, 1: always log and send even when GPS have no signal
LASS 設定。APP_ID 分為三種,system/public/provate,有不同的 base number
- //NOTICE: for Field TRY-PM2.5 DONT CHANGE AFTER THIS LINE! --2015/11/09
- //-----------------------------------------------------------
- //Step 5:About LASS
- #define APP_ID (APPTYPE_SYSTEM_BASE+1) // REPLACE: this is your unique application 0-255: system reserved, 256-32767: user public use, 32768-65536: private purpose
- #define APPTYPE_SYSTEM_BASE 0
- #define APPTYPE_PUBLIC_BASE 256
- #define APPTYPE_PRIVATE_BASE 32768
- #define SERIAL_BAUDRATE 115200
- //NOTICE: You are ready to ROCK, NO MORE TO SETUP!!!!
- //-----------------------------------------------------------
要不要發出警訊。包含蜂鳴器和做 MQTT callback
- //For ADVANCED user ONLY
- #define ALARM_ENABLE 0 // default(0) 0: disable alarm, 1: enable alarm
- #if ALARM_ENABLE==1
- #define BUZZER_ALARM_PIN 3
- #endif
感測器設定
- //----- USER SENSOR CONFIG -----
- #define SENSOR_CNT 25 // REPLACE: the sensors count that publish to server.
- #define SENSOR_STRING_MAX 300
- #define SENSOR_ID_RECORDID 0
- #define SENSOR_ID_BATTERYLEVEL 1
- #define SENSOR_ID_BATTERYCHARGING 2 // battery is charging: (0) not charging, (1) charging
- #define SENSOR_ID_GROUNDSPEED 3
- #define SENSOR_ID_DEBUGWIFI 4
這應該是 Arduino 內建特殊功能用的 pin
- //----- DEAFULT PIN DEFINE -----
- enum pinConfig{
- ARDUINO_LED_PIN = 13,
- STORAGE_CHIP_SELECT_PIN = 10
- };
這一大段是各個 "APP" 用的。第一個 #if 是 system 用的
- //LASS’s OPEN PM2.5 Field-TRY
- #if APP_ID==(APPTYPE_SYSTEM_BASE+1)
- #define USE_PM25_G3
- //#define USE_PM25_A4
- #define USE_DHT22 // not recommend for DHT series sensors
- //#define USE_SHT31
- #define SENSOR_ID_DUST 10
- #define SENSOR_ID_TEMPERATURE 11
- #define SENSOR_ID_HUMIDITY 12
- #define SENSOR_ID_DUST10 13
- #define SENSOR_ID_DUST_BLYNK 6
- #define SENSOR_ID_TEMPERATURE_BLYNK 7
- #define SENSOR_ID_HUMIDITY_BLYNK 8
- #define SENSOR_ID_DUST10_BLYNK 9
後面 #elif 是各個使用者定義的 public 區塊
- //LASS’s start up project by wuloong
- #elif APP_ID==(APPTYPE_PUBLIC_BASE+1)
- #define SENSOR_ID_DUST 10
- #define SENSOR_ID_UV 11
- #define SENSOR_ID_SOUND 12
- // in order to prevent blynk not support that many virtual gpio in the macro. we setup virtual GPIO in lower pin
- #define SENSOR_ID_DUST_BLYNK 4
- #define SENSOR_ID_UV_BLYNK 5
- #define SENSOR_ID_SOUND_BLYNK 6
- enum pinSensorConfig{
- DUST_SENSOR_PIN = 8,
- SOUND_SENSOR_PIN = A1,
- UV_SENSOR_PIN = A0,
- TEMP_HUMID_SENSOR_PIN = 2,
- };
- //LASS’s PM2.5 project by RODODO-MINGWEI
- #elif APP_ID==(APPTYPE_PUBLIC_BASE+2)
- #define SENSOR_ID_DUST 10
- #define SENSOR_ID_DUST10 13
- #define SENSOR_ID_DUST_BLYNK 4
- #define SENSOR_ID_DUST10_BLYNK 7
- //LASS project by Academia Sinica-LJ
- #elif APP_ID==(APPTYPE_PUBLIC_BASE+3)
- #define SENSOR_ID_BAROMETER 10
- #define SENSOR_ID_TEMPERATURE 11
- #define SENSOR_ID_HUMIDITY 12
- #define SENSOR_ID_LIGHT 13
- // in order to prevent blynk not support that many virtual gpio in the macro. we setup virtual GPIO in lower pin
- #define SENSOR_ID_BAROMETER_BLYNK 4
- #define SENSOR_ID_TEMPERATURE_BLYNK 5
- #define SENSOR_ID_HUMIDITY_BLYNK 6
- #define SENSOR_ID_LIGHT_BLYNK 7
- //#endif
- //LASS project by Academia Sinica-LJ
- #elif APP_ID==(APPTYPE_PUBLIC_BASE+4)
- #define USE_PM25_G3
- #define SENSOR_ID_BAROMETER 10
- #define SENSOR_ID_TEMPERATURE 11
- #define SENSOR_ID_HUMIDITY 12
- #define SENSOR_ID_LIGHT 13
- #define SENSOR_ID_DUST 14
- #define SENSOR_ID_DUST10 15
- // in order to prevent blynk not support that many virtual gpio in the macro. we setup virtual GPIO in lower pin
- #define SENSOR_ID_BAROMETER_BLYNK 4
- #define SENSOR_ID_TEMPERATURE_BLYNK 5
- #define SENSOR_ID_HUMIDITY_BLYNK 6
- #define SENSOR_ID_LIGHT_BLYNK 7
- #define SENSOR_ID_DUST_BLYNK 8
- #define SENSOR_ID_DUST10_BLYNK 9
- //#endif
- //LASS project by Air-1
- #elif APP_ID==(APPTYPE_PUBLIC_BASE+12)
- #error 請在此選擇你的感測器,選擇完請刪除此行。
- #define USE_PM25_G3
- //#define USE_PM25_A4
- #define USE_DHT22
- //#define USE_SHT31
- #define SENSOR_ID_DUST 10
- #define SENSOR_ID_TEMPERATURE 11
- #define SENSOR_ID_HUMIDITY 12
- #define SENSOR_ID_DUST10 13
- #define SENSOR_ID_NH3 14
- #define SENSOR_ID_CO 15
- #define SENSOR_ID_NO2 16
- #define SENSOR_ID_C3H8 17
- #define SENSOR_ID_C4H10 18
- #define SENSOR_ID_CH4 19
- #define SENSOR_ID_H2 20
- #define SENSOR_ID_C2H5OH 21
- #define SENSOR_ID_DUST_BLYNK 6
- #define SENSOR_ID_TEMPERATURE_BLYNK 7
- #define SENSOR_ID_HUMIDITY_BLYNK 8
- #define SENSOR_ID_DUST10_BLYNK 9
- #endif
系統其它參數設定。很多,看 code 的時候回來查就好
- //SYSTEM PARAMETERS
- #define DELAY_SYS_EARLY_WAKEUP_MS 11
- #define POLICY_ONLINE_ALWAYS 1
- #define POLICY_ONLINE_LESS 2
- #define POLICY_ONLINE_DEFAULT 0
- #define POLICY_POWER_DONTCARE 0
- #define POLICY_POWER_SAVE 1
- #define POLICY_POWER_AUTO 2
- #define LED_MODE_DEFAULT 0 // to show system status and behavior
- #define LED_MODE_OFF 1 // To not disturbe the environment, never have LED on
- #define ADJ_MODE_NORMAL 0 // default system behavior, no special adjustment
- #define ADJ_MODE_FLY 1 // for fly mode, speed up sensing, less wifi check
- #define SETTING_MODE_CODE // user setting from code
- #define SETTING_MODE_FLASH // user setting from flash
- #define SETTING_VERSION 2
- #define PERIOD_SENSING_IDX 0
- #define PERIOD_UPLOAD_IDX 1
- #define PERIOD_WIFICHECK_IDX 2
- #define POLICY_ONLINE POLICY_ONLINE_ALWAYS //1: POLICY_ONLINE_ALWAYS 2: POLICY_ONLINE_LESS
- #define POLICY_POWER POLICY_POWER_DONTCARE //2: POLICY_POWER_AUTO(Auto power saving mode) 0: POLICY_POWER_DONTCARE 1: POLICY_POWER_SAVE
- // policy auto check if not charging and battery lower than seting of battery level, switch to power saving mode.
- #define POWER_POLICY_BATTERY_LEVEL 70 // When battery level lower than this, trigger power saving mode when power policy is AUTO
- #define LED_MODE LED_MODE_DEFAULT
- #define ADJ_MODE ADJ_MODE_NORMAL // Some special adjustment for different scenario
- #define SETTING_MODE SETTING_MODE_CODE
- // The logic decide if we should do something
- #define LOGIC_WIFI_NEED_CONNECT 1
- #define LOGIC_MQTT_NEED_SEND 2
- #define LOGIC_DATA_NEED_SAVETOFLASH 3
- #define LOGIC_WHAT_LED_STATE 4
- #define LOGIC_LOG_NEED_SEND 5
- #define LED_STATE_OFF 0
- #define LED_STATE_READY 1
- #define LED_STATE_ERROR 2
2.3. Code Trace呼叫階層關係
BLYNK_READ (有一堆 XD)
- 設定手機介面需要的虛擬 sensor (看不懂,先跳過 XD)
msgCallback(): 由 PubSubClient callback
- * msgDisplay()
- * alarmHandlerCentral()
- * alarm_buzzer_set()
- * alarmHandlerPartner()
- * getComma()
- * getDoubleNumber()
- * getComma()
setting_verify(): setting init/save/load (debug 用平常不呼叫)
- * setting_init(), setting_show()
- * setting_save(), setting_show()
- * setting_load(), setting_show()
- * setting_versioncheck()
- * setting_init()
- * setting_save()
setup()
- * sensor_setup()
- * setup_mgs()
- * alarm_setup()
- * setting_load()
- * setting_versioncheck()
- * setting_init()
- * setting_save()
- * setting_init()
- * setting_save()
- * checkWifiConnected()
- * webserver_loop()
- * setting_save()
- * SendWebPage()
- * printwebWifiStatus()
- * lightBlink()
- * setting_show()
- * display_current_setting()
- * init_sensor_data()
- * retrieveNtpTime()
- * sendNTPpacket()
loop()
- * logic_select()
- * wifiConnecting()
- * Mtk_Wifi_Setup_TryCnt()
- * checkWifiConnected()
- * wifiConnected()
- * logic_select()
- * logSend()
- * checkWifiConnected()
- * mqttSubscribeRoutine()
- * mqttPublishRoutine()
- * blynk_setup()
- * //alarm_self_handler()
- * packInfo()
- * getCurrentTime()
- * parseGPGGA()
- * getComma()
- * getDoubleNumber()
- * getComma()
- * getIntNumber()
- * getComma()
- * parseGPRMC
- * getComma()
- * parseGPVTG()
- * getComma()
- * getDoubleNumber()
- * getComma()
- * get_sensor_data()
- * getBatteryStatus()
- * pm25sensorG3()
- * pm25sensorA4()
- * createThread()
- * dht_thread()
- * get_sensor_data_mgs()
- * get_sensor_data_dust()
- * pulseInNoWaitStart()
- * blynk_loop1()
- * get_sensor_data_uv()
- * get_sensor_data_sound()
- * get_sensor_data_barometer()
- * get_sensor_data_temperature()
- * get_sensor_data_humidity()
- * get_sensor_data_light()
- * checkWifiConnected()
- * mqttSubscribeRoutine()
- * mqttPrintCurrentMsg()
- * mqttPublishRoutine()
- * getBatteryStatus()
- * adjustCurrentPowerPolicy()
- * lightLed()
- * blynk_loop1()
2.4. 流程 (巨觀)
- setup() {
- 1. sensor_setup()
- * DHT22, SHT31, Gas, Sound, Dust, Light, Barometer, LCD...
- * 主要根據 APP_ID 的定義,決定 init 哪寫 sensors
- * 還有像是 USE_DHT22, USE_SHT31 的 define
- 2. alarm_setup()
- * 設定 buzzer
- 3. GPS power on
- 4. Flash init via chip-select opera
- 5. 從 internal flash 讀取 setting.bin。如果這次啟動有新的設定就覆寫。
- 6. 嘗試連線 WiFi
- 7. webserver_loop()
- * 好像是用來連到 MQTT server 的?
- 8. 把一些設定值 print 出來
- 9. 初始化 sensorType[ ] 填入 type 編號
- 10. 處理 FAKE_GPS
- }
setup() 裡判斷 APP_ID 的機制有點像是 profiles,不同的 user 可以自己定義 APP_ID。每個 APP_ID 可以對應不同的 sensors。例如有的人可能使用 DHT22 量測溫溼度,有的人則使用 SHT31。為什麼這樣設計,細節還不是很清楚。
- loop() {
- 1. logic_select(): 執行 WiFi 連線
- * 有點像 state machine,定義 5 種狀態
- * LOGIC_WIFI_NEED_CONNECT: 執行 WiFi 連線
- * LOGIC_MQTT_NEED_SEND: 透過 MQTT 傳送
- * LOGIC_DATA_NEED_SAVETOFLASH: 將資料存入 flash
- * LOGIC_WHAT_LED_STATE: 取得 LED 目前狀態
- * LOGIC_LOG_NEED_SEND: 傳送 log
- 2. 取得 GPS info
- 3. get_sensor_data(): 取得 sensors 資料
- * 更新 sensorValue[ ],放進 msg_sensor,再放入 sensorUploadString[ ]
- 4. packInfo(INFO_MQTT)
- * 將 sensorUploadString[ ] 放入 msg_tmp,再放入 msg
- * msg 變數的內容,就是最後會傳出去的 data
- 5. 當 need_send=1,把資料送出去 (MQTT publish)
- * logic_select(LOGIC_MQTT_NEED_SEND) 會決定何時讓它 =1 (還有省電模式,就是晚點送啦)
- 6. 將資料 save to flash
- 7. 取得電池資訊並更新 power policy (影響 delay time)
- }
loop() 從巨觀來看蠻合理的,WiFi 連線 -> 收資料 -> 傳出去,主要大概是這樣。我覺得 logic_select() 和 packInfo() 的作法應該可以更精簡。
另外,這篇: 如何加入新感測器 也稍微提到了幾個重點 function()
3. Data Format
應該要了解到底送了什麼資料出去,以及 LASS 是怎麼規劃資料格式的。
3.1. hackpad Ref
- Data Format
- Data specification
- Sensors data specification
3.2 MQTT Record definition
資料格式從 0.7.3 以後就是下面的樣子 (目前是 0.8.3。看 LASS.ino 的 VER_APP)
下面直接由 sample 說明。
前半段是一些 global data:
- |ver_format=3 # MQTT record format version
- ver_format=0 表示 user defined format
下面這兩個跟是否使用 fake GPS 資料有關,奇怪的是 code 裡面用上面的格式。文件說的卻是下面的格式?
- |FAKE_GPS=0 # 0: data from gps, 1: gps data is fix data
|fmt_opt=0 # 0: default, 1: gps info invalid (我猜是 FAKE_GPS 用的)
(updated) 這裡有說明 V0.8 後 Fake GPS說明。看起來 FAKE_GPS 才是目前的做法 (跟 code 一樣就對了 XD)
- |app=PM25 # system wide unique application name
- 所以不能跟其他人重複 (在 "App Lists" 註冊)
- |ver_app=0.7.3 # LASS version
- |device_id=LJ_PM25_001 # unique in your application
- |tick=26795810 # system tick (系統時鐘)
- |date=2015-10-15 # 從 gps info 拿到的 yyyy-mm-dd
- |time=22:35:07 # 從 gps info 拿到的 hh:mm:ss
- |device=LinkItONE # 目前有 {LinkItONE, Ameba}
s_n 是 “sensor type” 由 [type]+[sequence] 組成,目前的格式看起來有兩種:
- s_[c]: 1 個 char 代表是 system sensor (大多用 MTK 的 API 取得 data)。例如:
- s_0: 應該是傳送的序列號
- s_1: battery level
- s_2: battery mode {0: not charging 1:charging}
- s_3: ground speed (從 gps 取得)
- s_[cc]: 2 個 chars 代表是 user defined sensors
- 第一個 char 代表 sensor 類別
- b: barometer
- d: dust sensor
- g: gas related
- h: humidity
- l: light
- o: other, misc
- t: temperature
- w: wind
- r: rain
- 第二個 char 是 seq number, 從 0 開始
- 有點像 Linux device file 的 minor & major number XD
- 例如: (小心比對現有的編號,你應該優先使用現有的)
- s_d0: G3/G5 dust sensor PM2.5
- s_d3: Panasonic SN-GCHA1 dust sensor PM2.5
- s_g1: CO
- s_g8: SenseAir S8 CO2 (有的 define 很奇怪,到底是 by 不同氣體還是 by 不同 sensor)
- s_h0: DHT22 sensor
- s_h2: SHT31 sensor
- |s_0=2234.00
- |s_1=100.00
- |s_2=1.00
- |s_3=0.00
- |s_4=1 # 看 code 應該是 retry wifi connect 的次數?
- |s_d0=39.00 # G3/G5 dust sensor PM2.5
- |s_t0=25.50 # DHT22 sensor
- |s_h0=65.80 # DHT22 sensor (跟 t0 一樣,因為 DHT22 是 "溫濕度" 感應器)
GPS 有很多種格式,LASS 使用 GPGGA 當作預設的格式
- |gps_lat=25.025362 # latitude 緯度
- |gps_lon=121.371038 # longitude 經度
- |gps_fix=1 # fix quality? {0: invalid, 1: GPS fix, 2: DGPS fix}
- DGPS: 差分式全球定位系統 (其實我根本不知道這是啥)
- |gps_num=9 # 參與衛星定位的衛星數量
- |gps_alt=2 # 海平面以上高度
總之,最大的傳輸量是 512-1
- #define MSG_BUFFER_MAX 512
- char msg[MSG_BUFFER_MAX];
3.3. MQTT Topic definition
MQTT 發布或訂閱時需要知道的 topic name
- LASS 的定義為 “LASS/Group/APP”
- 例如:
- LASS/Test/Default (APP_ID=0)
- LASS/Test/PM25
- LASS/Test/Wuulong
- LASS/Test/LinkItSmart7688-Test
- LASS/Test/LASS4U
- 目前 code 裡面直接定義 MQTT_TOPIC_PREFIX “LASS/Test”
3.4. APP_ID
App Lists 可以看大家上傳檔案的格式。而且這個可以對照 configuration.h 那一堆 APP_ID #ifdef 看
概念是每個人的開發板可能都使用不同的 sensors 組合,而有不同的應用。所以這個 “App Lists” 頁面就是用來告訴大家目前的應用 APP(lication) 有哪些,以及該訂閱哪個 MQTT topic。
APP_ID 由 [application type] 和 [序列 number] 所組成。
application type
LASS 定義了三種 app type:
- System application
- 屬於 LASS 官方套件的,都歸在這一類。
- 目前有 PM25 和 LASS4U 都屬於 System APP
- Public application
- Wuulong、EXAMPLE_APP1、MAPS、FlyPm25…都屬於這一類,一般來說數量比較大
- Private application
APP_ID 最後的值,會落在三個區間。這三個區間表示他們所屬的 application type
- 0-255: System APP (APPTYPE_SYSTEM_BASE=0)
- 256-32767: Public APP (APPTYPE_PUBLIC_BASE=256)
- 32768-65536: Private APP (APPTYPE_PRIVATE_BASE=32768)
Note: 如果有新增,必須更新 App Lists
幾個特別的 APP_ID
LASS/configuration.h 裡面應該可以看到
- #if APP_ID==(APPTYPE_SYSTEM_BASE+1)
- #define USE_PM25_G3
- #define USE_DHT22
- ...
然後 LASS/LASS.ino 又有這一段
- #if (APP_ID==(APPTYPE_SYSTEM_BASE+1) || APP_ID==(APPTYPE_PUBLIC_BASE+12))
- #define APP_NAME "PM25" # 其實寫在 configuration.h 就好了呀 = =
- ...
APP_ID==(APPTYPE_SYSTEM_BASE+1) 其實 code 裡面就是 APP_ID=1,因為在 0~255 之間,所以它是一個 System APP。
看 code 可以知道,通常決定 APP_ID 之後還會決定一個 APP_NAME,APP_ID=1 的 APP_NAME=“PM25”。剛好 mapping 最後的 MQTT topic: LASS/Test/PM25
APP_ID 還有個比較特別的是 APP_ID=0, 它的 APP_NAME=“Default”,MQTT topic: LASS/Test/Default。它的內容應該只有四個:
- SENSOR_ID_RECORDID 0
- SENSOR_ID_BATTERYLEVEL 1
- SENSOR_ID_BATTERYCHARGING 2 // (0) not charging, (1) charging
- SENSOR_ID_GROUNDSPEED 3
- SENSOR_ID_DEBUGWIFI 4
其實正好就是對應到 system sensor 的 s_0, s_1, s_2, s_3, s_4。
Note: 其實文件中沒提到 s_4 但是 code 裡面有 @@!
一路看到這邊後,覺得 APP_ID 很奇怪,它似乎不再傳送的 MQTT 封包內容裡?
結果剛剛看到這篇: 如何加入新的 APP_ID
- APP_ID/APP_NAME 是在 LASS 用來區分應用情境的方式。APP_ID 和 APP_NAME 的對應是一對一的,
- APP_ID 基本上只使用在 Code 中,在 Data 是看不到的,
- APP_NAME 才是對使用者有意義的,也存在於 DATA 中。
看吧!!!
3.5. DEVICE_ID
Device ID 的定義在這: Device ID definition
編碼格式原則是 LASS-YYY_xxx
- YYY 為一到三碼
- xxx 為一碼含以上流水號
- 其中 LASS-TST_xxx 是測試用的,不能保證有唯一性 (看運氣 XD)
4. Others
LASS Specification 這份文件提到 LASS 系統巨觀應該提供怎樣的功能。若是想使用非 LinkItOne 的平台,應該儘可能地做到符合 LASS 的 spec.
- 小心得: 在 open source 又是多人參與的專案中,文件並不一定會一直同步更新或是持續被維護。看 source code 比較準!!
~ END ~