加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

項目分享 | 基于ELF 1開發(fā)板的MQTT遠(yuǎn)程溫濕度監(jiān)測系統(tǒng)

04/26 08:43
2448
閱讀需 28 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

今天非常榮幸向各位小伙伴詳細(xì)展示一個由共創(chuàng)社成員完成的MQTT遠(yuǎn)程溫濕度監(jiān)控系統(tǒng)項目。該項目借助ELF 1開發(fā)板作為核心技術(shù)支撐,成功實現(xiàn)了對各類環(huán)境空間中溫濕度數(shù)據(jù)的實時、遠(yuǎn)程、穩(wěn)定監(jiān)測。該系統(tǒng)不僅集成了先進(jìn)的數(shù)據(jù)采集模塊,用于精確感知現(xiàn)場環(huán)境變化,同時利用MQTT協(xié)議的輕量級特性,確保了數(shù)據(jù)在復(fù)雜網(wǎng)絡(luò)環(huán)境下的可靠傳輸。在此接下來,就為各位小伙伴詳盡展示這一項目的相關(guān)細(xì)節(jié)。

1、Linux開發(fā)板開發(fā)環(huán)境搭建

(1)開發(fā)板動態(tài)分配ip地址

(開發(fā)板與家用路由器連接,路由器支持DHCP自動IP地址分配)

 root@ELF1:~# udhcpc -i eth0   

(2)將nfs服務(wù)器掛載到開發(fā)板的/mnt目錄

(其中“192.168.1.10”是Ubuntu的ens36的ip)

root@ELF1:~# mount -t nfs -o nolock,vers=3 192.168.1.10:/home/book/nfs_rootfs /mnt

可以看到開發(fā)板的/mnt目錄已經(jīng)有了文件。

2、Ubuntu編譯環(huán)境搭建:

(1)將paho mqtt的官方庫克隆到Ubuntu的“~/nfs_rootfs”路徑

book@100ask:~/nfs_rootfs$ git clone https://github.com/eclipse/paho.mqtt.c.git

(2)修改“~/nfs_rootfs/paho_mqtt/paho.mqtt.c”路徑下的Makefile文件

修改prefix所代表的工具鏈路徑:

修改編譯器

(3)編譯后得到鏈接庫

book@100ask:~/nfs_rootfs/paho_mqtt/paho.mqtt.c$ make

arm-gcc編譯生成的.so庫文件,保存在paho.mqtt.c/build/output里面。

將.so庫文件安裝到本地PC:

book@100ask:~/nfs_rootfs/paho_mqtt/paho.mqtt.c$ sudo make install

(4)將.so庫文件安裝到開發(fā)板的“/lib”路徑,開發(fā)板才能運(yùn)行paho mqtt編譯后的可執(zhí)行文件

root@ELF1:~# install /mnt/paho_mqtt/paho.mqtt.c/build/output/libpaho-mqtt3* /lib
以上開發(fā)環(huán)境搭建完成!

3、工程文件的建立

(1)將“~/nfs_rootfs/paho_mqtt/paho.mqtt.c”

路徑中的src文件夾拷貝到“~/nfs_rootfs/mqtt_iot”路徑下

 book@100ask:~$ cp -r ~/nfs_rootfs/paho_mqtt/paho.mqtt.c ~/nfs_rootfs/mqtt_iot

(2)在“~/nfs_rootfs/mqtt_iot”路徑下添加文件:

4、阿里云服務(wù)器設(shè)置:

(1)服務(wù)器的注冊及產(chǎn)品、設(shè)備設(shè)置可以參照ElfBoard官方文檔:《01-1 ELF1、ELF1S開發(fā)板_軟件教程_V1》第5章-5.4小節(jié)。

(2)工程文件中所需要的服務(wù)器參數(shù)查找

查看服務(wù)器地址

查看DeviceName、DeviceSecret

查看MQTT連接參數(shù)

5.工程文件的編譯執(zhí)行

(1)編譯工程文件,得到可在Linux開發(fā)板上執(zhí)行的Main二進(jìn)制文件

book@100ask:~/nfs_rootfs/mqtt_iot$ arm-buildroot-linux-gnueabihf-gcc  main.c mqtt_iot.c -o main -lpaho-mqtt3c -lpthread

MQTT的異步通信收發(fā),依賴的庫是libpaho-mqtt3a,MQTT的同步通信收發(fā),依賴的庫就是libpaho-mqtt3c。此外工程編譯的時候需要鏈接線程的庫pthread,所以編譯的時候要加上-lpthread。

(2)開發(fā)板運(yùn)行程序

root@ELF1:/mnt/mqtt_iot# ./main

(3)實驗結(jié)果

Linux開發(fā)板將采集到的溫度、濕度數(shù)據(jù)每5s上傳一次阿里服務(wù)器,串口窗口顯示數(shù)據(jù)發(fā)送成功字符。此外通過阿里服務(wù)器日志服務(wù)可以看到濕度、溫度數(shù)據(jù)。

通過阿里服務(wù)器調(diào)試窗口給開發(fā)板發(fā)送LED1、LED2控制指令。

6、下面將貼出工程文件的代碼,并介紹其思路

(1)main.c文件

//main.c
//定義線程句柄
pthread_t discon_t;
pthread_t thread_AT20Read_t;
pthread_t thread_ledctrl_t;

static int isConnected = 0;//表明客戶端和服務(wù)器是斷開還是連接狀態(tài)(1-連接狀態(tài),-1斷開狀態(tài))

static void *thread_AT20Read(void *paramater)
{
    int fd = -1;
  unsigned int databuf[2];
  int c1,t1;
  float hum,temp;
  int ret = 0;
    msgbuf pubMsg = {2, 0};

    while(fd < 0){
        fd = open(AHT20_DEV, O_RDWR);
        if(fd < 0){
            printf("can't open file %srn", AHT20_DEV);
            sleep(1);
        }else{
            printf("open file %s successfullyrn", AHT20_DEV);
        }
    }

  while(1){
    ret = read(fd, databuf, sizeof(databuf));
    if(ret == 0){

            c1 = databuf[0]*1000/1024/1024;
            t1 = databuf[1] *200*10/1024/1024-500;
            hum = (float)c1/10.0;
            temp = (float)t1/10.0;
            //printf("hum = %0.2f temp = %0.2f rn",hum,temp);

            pubMsg.mtext[0] = (unsigned int)(hum*100);
            pubMsg.mtext[1] = (unsigned int)(temp*100);
            int ret1 = msgsnd(pubmsg_d, &pubMsg.mtype, sizeof(pubMsg.mtext), IPC_NOWAIT);  // 非阻塞發(fā)送
            if(ret1 != 0)
            {
                printf("Failed to send message.rn");
            }

    }
        sleep(5);
  }
}

static void *thread_ledctrl(void *paramater)
{
  int on=1;
  int led;
  int fd;
    msgbuf subMsg = {1, 0};
  fd = open(LED_BRIGHTNESS, O_WRONLY);

  if(fd < 0)
  {
      perror("open device leds");
      exit(1);
  }
    system(LED1_OFF);
  system(LED2_OFF);
  while(1)
  {
        int res = msgrcv(submsg_d, &subMsg, sizeof(subMsg.mtext), 0, 0);//阻塞
        if(res < 0)
            continue;
        else{
            if((subMsg.mtext[0] & 0x01)== 1){
                system(LED1_ON);
            }else{
                system(LED1_OFF);
            }

            if((subMsg.mtext[0] & 0x02)== 0x02){
                system(LED2_ON);
            }else{
                system(LED2_OFF);
            }
        }
  }

}

//斷開和mqtt服務(wù)器連接的線程入口函數(shù)
static void *mqtt_disconnect_t(void* argv)
{
    int retval;
    while(1)
    {
        char ch;
        ch = getchar();
        if(ch=='Q' || ch=='q')
        {
            printf("Try to exit mqtt taskn");
            if(mqtt_disconnect() == EXIT_SUCCESS)   break;
        }
    }
    isConnected = -1;
    pthread_exit(&retval);  // 退出線程
    return NULL;
}

int main(void)
{
    //初始化mqtt成功建立客戶端和服務(wù)器的連接后,將主動斷開服務(wù)器的任務(wù)放到一個線程里面去
    //成功建立客戶端和服務(wù)器的連接且訂閱主題后才創(chuàng)建斷開連接的線程
    if(mqtt_iot() == 0)
    {
        isConnected = 1;
        pthread_create(&discon_t, 0, mqtt_disconnect_t, NULL);
    }

    //AT20 read thread
    int ret = pthread_create(&thread_AT20Read_t, NULL, thread_AT20Read, NULL);
    if(ret != 0)
    {
        printf("Failed to create AT20Read thread.n");
        return -1;
    }

    //led control thread
    ret = pthread_create(&thread_ledctrl_t, NULL, thread_ledctrl, NULL);
    if(ret != 0)
    {
        printf("Failed to create ledctrl thread.n");
        return -1;
    }

    while(1)
    {
        //printf("isConnected state:%dn",isConnected);
        sleep(5);
    }

    return 0;
}

(2)mqtt_iot.c文件

//mqtt_iot.c
volatile MQTTClient_deliveryToken deliveredtoken;

pthread_t threads[2];
sem_t discon_sem;//信號量
int pubmsg_d = -1;
int submsg_d = -1;
msgbuf subMsg = {1, 0};
msgbuf pubMsg = {2, 0};
pthread_t thread_mqtt_publish_t;

MQTTClient client;  //定義一個MQTT客戶端client
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;


//傳遞給MQTTClient_setCallbacks的回調(diào)函數(shù),消息發(fā)送成功后,調(diào)用此回調(diào)函數(shù)
void delivered(void *context, MQTTClient_deliveryToken dt)
{
  printf("Message with token value %d delivery confirmedn", dt);
  deliveredtoken = dt;
}

//傳遞給MQTT-Client_setCallbacks的回調(diào)函數(shù) 消息到達(dá)后,調(diào)用此回調(diào)函數(shù)
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
  printf("---------------------------------------------------------------n");
  printf("Message arrivedn");
  printf(" topic: %sn", topicName);
  printf(" message: %.*sn", message->payloadlen, (char*)message->payload);
  printf("---------------------------------------------------------------n");

  subMsg.mtext[0] = 0;
  unsigned short len = message->payloadlen;
  char *buf = (char*)message->payload;
  for(unsigned short i=0; i<len; i++)
  {
    if(buf[i] == '')  break;
    if(buf[i]<='9' && buf[i]>='0')
      subMsg.mtext[0] = subMsg.mtext[0]*10 + buf[i] - '0';
  }

  int ret = msgsnd(submsg_d, &subMsg.mtype, sizeof(subMsg.mtext), IPC_NOWAIT);  // 非阻塞發(fā)送
  if(ret != 0)
  {
    printf("Failed to send message.rn");
  }

  MQTTClient_freeMessage(&message);  // 釋放消息
  MQTTClient_free(topicName);  // 釋放主題名
  return 1;
}

//傳遞給MQTTClient_setCallbacks的回調(diào)函數(shù) 連接異常斷開后調(diào)用此回調(diào)函數(shù)
void connlost(void *context, char *cause)
{
  printf("nConnection lostn");
  printf(" cause: %sn", cause);
}

//實現(xiàn)MQTT的發(fā)布
void *mqtt_publish(void *argv)
{
  MQTTClient_message pubmsg = MQTTClient_message_initializer;
  MQTTClient_deliveryToken token;
  char data[9];
  int rc;
  pubmsg.qos = QOS;
  pubmsg.retained = 0;
  while(1)
    {
    //接收消息(消息隊列的ID,存放消息的指針,指定接收消息的大小,0-讀取消息隊列中第一個數(shù)據(jù),阻塞)
    int res = msgrcv(pubmsg_d, &pubMsg, sizeof(pubMsg.mtext), 0, 0);
    if(res < 0)  continue;
    {
      //printf("Publish_hum: %dn", pubMsg.mtext[0]);
      //printf("Publish_temp: %dn", pubMsg.mtext[1]);
      sprintf(data, "%d,%d", pubMsg.mtext[0],pubMsg.mtext[1]);
      pubmsg.payload = data;
      pubmsg.payloadlen = sizeof(data);
      if((rc = MQTTClient_publishMessage(client, PUB_TOPIC, &pubmsg, &token)) != MQTTCLIENT_SUCCESS)
      {
        printf("Failed to publish message, return code %dn", rc);
        break;
      }
      rc = MQTTClient_waitForCompletion(client, token, TIMEOUT);
      printf("Message with delivery token %d deliveredn", token);
    }
  }

    if((rc = MQTTClient_disconnect(client, 10000)) != MQTTCLIENT_SUCCESS)  //斷開和服務(wù)器的連接
  {
    printf("Failed to disconnect, return code %dn", rc);
  }
    pthread_exit(&threads[PubThread]);
  return NULL;
}

//封裝主動斷開連接服務(wù)器的函數(shù)
int mqtt_disconnect(void)
{
    int rc = EXIT_SUCCESS;

  //兩個參數(shù):MQTT客戶端和斷開連接超時時間
  if ((rc = MQTTClient_disconnect(client, 10000)) != MQTTCLIENT_SUCCESS)  //斷開和服務(wù)器的連接
  {
    printf("Failed to disconnect, return code %dn", rc);
    rc = EXIT_FAILURE;
  }
  else
  {
    printf("MQTT disconnect successn");
    MQTTClient_destroy(&client);
  }

  return rc;
}

// mqtt建立客戶端、連接服務(wù)器、訂閱主題的封裝入口函數(shù)
int mqtt_iot(void)
{
    int rc = EXIT_SUCCESS;
  //創(chuàng)建客戶端
  if ((rc = MQTTClient_create(&client, ADDRESS, CLIENTID,
          MQTTCLIENT_PERSISTENCE_NONE, NULL)) != MQTTCLIENT_SUCCESS)
  {
    printf("Failed to create client, return code %dn", rc);
    goto exit;
  }

  //設(shè)置回調(diào)函數(shù)(連接丟失處理回調(diào)函數(shù),處理訂閱消息的回調(diào)函數(shù),成功發(fā)布消息后的回調(diào)函數(shù))
  if((rc = MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered)) != MQTTCLIENT_SUCCESS)
  {
    printf("Failed to set callbacks, return code %dn", rc);
    goto destroy_exit;
  }

    conn_opts.username = USERNAME;
    conn_opts.password = PASSWORD;
  conn_opts.keepAliveInterval = 60;//保活周期,客戶端向服務(wù)器發(fā)送心跳包的周期,單位秒
  conn_opts.cleansession = 1;
  //連接服務(wù)器
  if((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
  {
    printf("Failed to connect, return code %dn", rc);
    goto destroy_exit;
  }

    //訂閱主題(傳入客戶端句柄、訂閱的主題以及消息質(zhì)量)
  if ((rc = MQTTClient_subscribe(client, SUB_TOPIC, QOS)) != MQTTCLIENT_SUCCESS)
  {
    printf("Failed to subscribe, return code %dn", rc);
        goto destroy_exit;
  }

  //初始化信號量
  if(sem_init(&discon_sem, 1, 0) != 0)
  {
    printf("Failed to init semaphoren");
    return -1;
  }
  //創(chuàng)建隊列
  pubmsg_d = msgget(0x1234, IPC_CREAT);
  submsg_d = msgget(0x5678, IPC_CREAT);
  if(pubmsg_d == -1 || submsg_d==-1)  //返回錯誤碼-1
  {
    printf("Failed to create a mqtt message, pubid:%d, subid:%dn", pubmsg_d, submsg_d);
    return -1;
  }
  else
  {
    printf("Publish message id: %dn", pubmsg_d);
    printf("Subscribe message id: %dn", submsg_d);
  }

    int ret = pthread_create(&thread_mqtt_publish_t, NULL, mqtt_publish, NULL);
    if(ret != 0)
    {
        printf("Failed to create mqtt_publish thread.n");
        return -1;
    }

  printf("MQTT connect success, press 'Q' or 'q' to disconnect mqtt servern");
    return 0;

destroy_exit:
  MQTTClient_destroy(&client); //釋放客戶端的資源, 參數(shù)-同步客戶端的句柄
    return -1;
exit:
    return -1;
} 

至此,就完成了關(guān)于ELF 1開發(fā)板研發(fā)的MQTT遠(yuǎn)程溫濕度監(jiān)測系統(tǒng)介紹。希望這套實踐案例能夠成為各位小伙伴的寶貴參考,啟迪創(chuàng)新思維,推進(jìn)各位嵌入式愛好者在學(xué)習(xí)的道路上不斷前進(jìn)。

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風(fēng)險等級 參考價格 更多信息
511BCA100M000BAG 1 Silicon Laboratories Inc Oscillator, 0.1MHz Min, 250MHz Max, 100MHz Nom,

ECAD模型

下載ECAD模型
$4.11 查看
CPC1560G 1 IXYS Corporation Transistor Output SSR, 1-Channel, 3750V Isolation, DIP-8
$4.67 查看
Q13MC1462000200 1 Seiko Epson Corporation Parallel - Fundamental Quartz Crystal, 0.032768MHz Nom,
$1 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜