LASS - 1

LASS 

此範例使用實驗版 SDK, 實驗版方式,

在偏好設定的 板子管理員網址 (Additional Board Manager) 輸入:
 https://github.com/neojou/arduino-ameba/raw/master/release/package_njiot.com_ameba_index.json


在做這個 LASS 範例之前, 請先做底下幾個範例

1. PM2.5 sensor

2. Wifi NTP 讀取時間

3. MQTT example


如同 MQTT 範例, 我們先用 Google 的 MQTTLens 來連接 LASS server
gpssensor.ddns.net

看看



如果有顯示 connected, 表示目前網路環境是可以連線的.



LASS PM2.5 arduino program

https://github.com/neojou/arduino-ameba/blob/master/example/lass-pm25/lass-pm25.ino



/*
This example demonstrate how to upload sensor data to MQTT server of LASS.
It include features:
(1) Connect to WiFi
(2) Retrieve NTP time with WiFiUDP
(3) Get PM 2.5 value from PMS3003 air condition sensor with UART
(4) Optional DHT support ,comment #define USE_DHT
(5) Connect to MQTT server and try reconnect when disconnect
https://github.com/LinkItONEDevGroup/LASS
*/
#include <WiFi.h>
#include <PubSubClient.h>
#include <WiFiUdp.h>
//#define USE_DHT
#ifdef USE_DHT
#include "DHT.h"
#endif
char ssid[] = "YourWiFiAPName"; // your network SSID (name)
char pass[] = "YourWiFiAPPass"; // your network password
char gps_lat[] = "24.7805647"; // device's gps latitude
char gps_lon[] = "120.9933177"; // device's gps longitude
#ifdef USE_DHT
#define DHTPIN 2 // what digital pin we're connected to
#define DHTTYPE DHT11 // DHT 22 (AM2302), AM2321
//#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
DHT dht(DHTPIN, DHTTYPE);
#endif
const char server[] = "gpssensor.ddns.net"; // the MQTT server of LASS
const char ntpServer[] = "pool.ntp.org";
#define MAX_CLIENT_ID_LEN 10
#define MAX_TOPIC_LEN 50
char clientId[MAX_CLIENT_ID_LEN];
char outTopic[MAX_TOPIC_LEN];
void MQTTcallback(char* topic, byte* payload, unsigned int length)
{
Serial.print(F("Message arrived ["));
Serial.print(topic);
Serial.print(F("] "));
for (int i=0;i<length;i++) {
Serial.print((char)payload[i]);
}
Serial.println();
}
WiFiClient wifiClient;
PubSubClient mqttclient((char*)server, 1883, MQTTcallback, wifiClient);
WiFiUDP Udp;
void initializeWiFi()
{
int status = WL_IDLE_STATUS;
// attempt to connect to Wifi network:
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
Serial.println("WiFi connected , wait for 10 sec to get IP");
delay(10000);
}
// DHCP IP
Serial.println(F("IP address: "));
Serial.println(WiFi.localIP());
// local port to listen for UDP packets
Udp.begin(2390);
}
//NTP
unsigned int localPort = 2390; // local port to listen for UDP packets
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
const byte nptSendPacket[ NTP_PACKET_SIZE] = {
0xE3, 0x00, 0x06, 0xEC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x4E, 0x31, 0x34,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
byte ntpRecvBuffer[ NTP_PACKET_SIZE ];
uint32_t epochSystem = 0; // timestamp of system boot up
#define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0
void retrieveNtpTime(){
Serial.println("Send NTP packet");
Udp.beginPacket(ntpServer, 123); //NTP requests are to port 123
Udp.write(nptSendPacket, NTP_PACKET_SIZE);
Udp.endPacket();
if(Udp.parsePacket()) {
Serial.println("NTP packet received");
Udp.read(ntpRecvBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
unsigned long highWord = word(ntpRecvBuffer[40], ntpRecvBuffer[41]);
unsigned long lowWord = word(ntpRecvBuffer[42], ntpRecvBuffer[43]);
unsigned long secsSince1900 = highWord << 16 | lowWord;
const unsigned long seventyYears = 2208988800UL;
unsigned long epoch = secsSince1900 - seventyYears;
epochSystem = epoch - millis() / 1000;
}
}
//Sensor
long pmcf10=0;
long pmcf25=0;
long pmcf100=0;
long pmat10=0;
long pmat25=0;
long pmat100=0;
void retrievePM25Value()
{
int count;
unsigned char c;
unsigned char high;
count=0;
while (Serial1.available()) {
c = Serial1.read();
if((count==0 && c!=0x42) || (count==1 && c!=0x4d)){
Serial.println(F("#[G3-ERROR-CHECKSUM]"));
break;
}
if(count > 15){
Serial.println("G3 complete");
break;
}
else if(count == 4 || count == 6 || count == 8 || count == 10 || count == 12 || count == 14) {
high = c;
}
else if(count == 5){
pmcf10 = 256*high + c;
Serial.print("CF=1, PM1.0=");
Serial.print(pmcf10);
Serial.println(" ug/m3");
}
else if(count == 7){
pmcf25 = 256*high + c;
Serial.print("CF=1, PM2.5=");
Serial.print(pmcf25);
Serial.println(" ug/m3");
}
else if(count == 9){
pmcf100 = 256*high + c;
Serial.print("CF=1, PM10=");
Serial.print(pmcf100);
Serial.println(" ug/m3");
}
else if(count == 11){
pmat10 = 256*high + c;
Serial.print("atmosphere, PM1.0=");
Serial.print(pmat10);
Serial.println(" ug/m3");
}
else if(count == 13){
pmat25 = 256*high + c;
Serial.print("atmosphere, PM2.5=");
Serial.print(pmat25);
Serial.println(" ug/m3");
}
else if(count == 15){
pmat100 = 256*high + c;
Serial.print("atmosphere, PM10=");
Serial.print(pmat100);
Serial.println(" ug/m3");
}
count++;
}
while(Serial1.available()) Serial1.read();
}
void initializeMQTT() {
byte mac[6];
WiFi.macAddress(mac);
memset(clientId, 0, MAX_CLIENT_ID_LEN);
sprintf(clientId, "FT2_0%02X%02X", mac[4], mac[5]);
sprintf(outTopic, "LASS/Test/Pm25Ameba/%s", clientId);
Serial.print("MQTT client id:");
Serial.println(clientId);
Serial.print("MQTT topic:");
Serial.println(outTopic);
}
void reconnectMQTT() {
// Loop until we're reconnected
while (!mqttclient.connected()) {
Serial.print(F("Attempting MQTT connection..."));
// Attempt to connect
if (mqttclient.connect(clientId)) {
Serial.println(F("connected"));
mqttclient.subscribe(outTopic);
} else {
Serial.println(F("Failed... try again in 5 seconds"));
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void getCurrentTime(unsigned long epoch, int *year, int *month, int *day, int *hour, int *minute, int *second) {
int tempDay = 0;
*hour = (epoch % 86400L) / 3600;
*minute = (epoch % 3600) / 60;
*second = epoch % 60;
*year = 1970;
*month = 0;
*day = epoch / 86400;
for (*year = 1970; ; (*year)++) {
if (tempDay + (LEAP_YEAR(*year) ? 366 : 365) > *day) {
break;
} else {
tempDay += (LEAP_YEAR(*year) ? 366 : 365);
}
}
tempDay = *day - tempDay; // the days left in a year
for ((*month) = 0; (*month) < 12; (*month)++) {
if ((*month) == 1) {
if (LEAP_YEAR(*year)) {
if (tempDay - 29 < 0) {
break;
} else {
tempDay -= 29;
}
} else {
if (tempDay - 28 < 0) {
break;
} else {
tempDay -= 28;
}
}
} else {
if (tempDay - monthDays[(*month)] < 0) {
break;
} else {
tempDay -= monthDays[(*month)];
}
}
}
(*month)++;
*day = tempDay+2; // one for base 1, one for current day
}
char payload[300];
void sendMQTTMessage(){
unsigned long epoch = epochSystem + millis() / 1000;
int year, month, day, hour, minute, second;
getCurrentTime(epoch, &year, &month, &day, &hour, &minute, &second);
sprintf(payload, "|ver_format=3|fmt_opt=1|app=Pm25Ameba|ver_app=0.0.1|device_id=%s|tick=%d|date=%4d-%02d-%02d|time=%02d:%02d:%02d|device=Ameba|s_d0=%d|gps_lat=%s|gps_lon=%s|gps_fix=1|gps_num=9|gps_alt=2",
clientId,
millis(),
year, month, day,
hour, minute, second,
pmat25,
gps_lat, gps_lon
);
// Once connected, publish an announcement...
mqttclient.publish((char*)outTopic,payload);
Serial.print(outTopic);
Serial.println(payload);
}
void setup()
{
Serial.begin(9600);
delay(10);
initializeWiFi();
retrieveNtpTime();
initializeMQTT();
#ifdef USE_DHT
dht.begin();
#endif
Serial1.begin(9600); // PMS 3003 UART has baud rate 9600
}
void loop()
{
#ifdef USE_DHT
h = dht.readHumidity();
t = dht.readTemperature();
if(isnan(h) || isnan(t)) { h=-1;t=-1;}
#endif
retrievePM25Value();
if (pmat25 == 0 ) {
Serial.println("try to read PM25 sensor later");
delay(5000);
return;
}
//Process Filter or any logic control below
if(mqttclient.connected()){
sendMQTTMessage();
mqttclient.loop();
delay(60000);
} else {
reconnectMQTT();
delay(1000);
return;
}
}

程式說明 :

1. 時間
    如先前 NTP 範例, 我們只有在一開始時 setup() , 用 NTP 抓時間.
    之後用 系統 millis() 和 NTP 時間 相加 計算.  getCurrentTime()
    將來如果搭配省電, 可以外接 RTC 定時喚醒/或重開主系統

2. LASS MQTT 資料格式

https://lass.hackpad.com/LASS-Data-specification-1dYpwINtH8R

   程式是在 sendMQTTMessage() ,
   用 sprinf 把資料按格式寫到  payload 後,
   用 MQTT publish() 將 payload 送出

mqttclient.publish((char*)outTopic,payload);

3. 溫度
之後 用 DHT sensor 把溫度補上

4. GPS 位置.
    目前先用 google map 把 GPS 位置手寫填到 gps_lat 和 gps_lon 變數中
char gps_lat[] = "24.7805647"; // device's gps latitude
char gps_lon[] = "120.9933177"; // device's gps longitude
    之後接 GPS 把資料補上

5. device ID

會拿這片卡的 MAC 最後兩碼來組合
sprintf(clientId, "FT2_0%02X%02X", mac[4], mac[5]);



執行結果 :

從log 可以看到

MQTT client id:FT2_0F0EC


可以從底下這個 link 看到

http://nrl.iis.sinica.edu.tw/LASS/show.php?device_id=FT2_0F0EC


之後來變成長期觀測站時, 可以來研究修改一下. 讓底下這個站顯示

http://iot.sparkfuture.io/pm25


留言

熱門文章