STM32+W5500+MQTT+Android實現遠端資料採集及控制
轉自:http://www.embed-net.com/thread-230-1-1.html
0 前言
最近在學習MQTT,發現MQTT還是挺好用的,於是花了點時間做了一個簡單的應用示例,希望能給需要做這方面的人一些參考。
相關背景知識:http://www.embed-net.com/thread-224-1-1.html
具體功能為:
1,STM32F405為主控晶片,它通過感測器採集環境資料,比如溫度,溼度,光照度,大氣壓強等;
2,主控晶片通過W5500模組將測量的資料通過MQTT協議方式釋出到MQTT伺服器(伺服器域名和IP見韌體程式);
3,主控訂閱LED燈控制的訊息,當接收到對應的控制指令後點亮或者熄滅對應的LED燈;
4,安卓手機端訂閱感測器資料的訊息,當接收到訊息後將感測器資料在介面顯示;
5,安卓手機可傳送點亮或者熄滅LED燈的指令到伺服器,然後伺服器會將該指令轉發給STM32主控,然後STM32主控解析該指令並執行指令。
1
微控制器端實現
MQTT協議是基於TCP的協議,所以我們只需要在微控制器端實現TCP客戶端程式碼之後就很容易移植MQTT了,STM32F4+W5500實現TCP客戶端的程式碼我們以前已經實現過,程式碼下載地址為:
http://www.embed-net.com/thread-87-1-1.html
當然,如果你想在程式碼裡面直接使用伺服器域名方式進行連線,我們還得在TCP客戶端程式碼裡面整合DNS的程式碼,當然在上面這個連線裡面也有相關的程式碼。
MQTT程式碼原始碼下載地址:
http://www.eclipse.org/paho/
在STM32這邊我們使用的是C/C++ MQTT Embedded clients程式碼。
硬體連線如下圖所示:
1.1
MQTT的移植
MQTT的移植非常簡單,將C/C++ MQTT Embedded clients的程式碼新增到工程中,然後我們只需要再次封裝4個函式即可:
int transport_sendPacketBuffer(unsigned char* buf, int buflen);
int transport_getdata(unsigned char* buf, int count);
int transport_open(void);
int transport_close(void);
transport_sendPacketBuffer:通過網路以TCP的方式傳送資料;
transport_getdata:TCP方式從伺服器端讀取資料,該函式目前屬於阻塞函式;
transport_open:開啟一個網路介面,其實就是和伺服器建立一個TCP連線;
transport_close:關閉網路介面。
如果已經移植好了socket方式的TCP客戶端的程式,那麼這幾個函式的封裝也是非常簡單的,程式程式碼如下所示:
/**
* @brief 通過TCP方式傳送資料到TCP伺服器
* @param buf 資料首地址
* @param buflen 資料長度
* @retval 小於0表示傳送失敗
*/
int transport_sendPacketBuffer(unsigned char* buf, int buflen)
{
return send(SOCK_TCPS,buf,buflen);
}
/**
* @brief 阻塞方式接收TCP伺服器傳送的資料
* @param buf 資料儲存首地址
* @param count 資料緩衝區長度
* @retval 小於0表示接收資料失敗
*/
int transport_getdata(unsigned char* buf, int count)
{
return recv(SOCK_TCPS,buf,count);
}
/**
* @brief 開啟一個socket並連線到伺服器
* @param 無
* @retval 小於0表示開啟失敗
*/
int transport_open(void)
{
int32_t ret;
//新建一個Socket並繫結本地埠5000
ret = socket(SOCK_TCPS,Sn_MR_TCP,5000,0×00);
if(ret != SOCK_TCPS){
printf(“%d:Socket Error\r\n”,SOCK_TCPS);
while(1);
}else{
printf(“%d:Opened\r\n”,SOCK_TCPS);
}
//連線TCP伺服器
ret = connect(SOCK_TCPS,domain_ip,1883);//埠必須為1883
if(ret != SOCK_OK){
printf(“%d:Socket Connect Error\r\n”,SOCK_TCPS);
while(1);
}else{
printf(“%d:Connected\r\n”,SOCK_TCPS);
}
return 0;
}
/**
* @brief 關閉socket
* @param 無
* @retval 小於0表示關閉失敗
*/
int transport_close(void)
{
close(SOCK_TCPS);
return 0;
}
完成了這幾個函式,然後我們就可以根據官方提供的示例程式碼實現我們自己的程式碼了,比如我們向代理伺服器傳送一個訊息的程式碼如下所示:
/**
* @brief 向代理(伺服器)傳送一個訊息
* @param pTopic 訊息主題
* @param pMessage 訊息內容
* @retval 小於0表示傳送失敗
*/
int mqtt_publish(char *pTopic,char *pMessage)
{
int32_t len,rc;
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
unsigned char buf[200];
MQTTString topicString = MQTTString_initializer;
int msglen = strlen(pMessage);
int buflen = sizeof(buf);
data.clientID.cstring = “me”;
data.keepAliveInterval = 5;
data.cleansession = 1;
len = MQTTSerialize_connect(buf, buflen, &data); /* 1 */
topicString.cstring = pTopic;
len += MQTTSerialize_publish(buf + len, buflen – len, 0, 0, 0, 0, topicString, (unsigned char*)pMessage, msglen); /* 2 */
len += MQTTSerialize_disconnect(buf + len, buflen – len); /* 3 */
transport_open();
rc = transport_sendPacketBuffer(buf,len);
transport_close();
if (rc == len)
printf(“Successfully published\n\r”);
else
printf(“Publish failed\n\r”);
return 0;
}
下面我們看下主函式的程式碼,思路也比較清晰:
int main(void)
{
static char meassage[200];
int rc;
char *led;
char led_value;
float temperature,humidity,light,pressure;
srand(0);
//配置LED燈引腳
LED_Config();
//初始化配置網路
network_init();
while(1){
memset(meassage,0,sizeof(meassage));
//訂閱訊息
rc = mqtt_subscrib(“pyboard_led”,meassage);
printf(“rc = %d\n\r”,rc);
if(rc >= 0){
printf(“meassage = %s\n\r”,meassage);
//解析JSON格式字串並點亮相應的LED燈
cJSON *root = cJSON_Parse(meassage);
if(root != NULL){
led = cJSON_GetObjectItem(root,”led”)->valuestring;
printf(“led = %s\n\r”,led);
led_value = cJSON_GetObjectItem(root,”value”)->valueint;
if(!strcmp(led,”red”)){
if(led_value){
LED_On(LED_RED);
}else{
LED_Off(LED_RED);
}
}else if(!strcmp(led,”green”)){
if(led_value){
LED_On(LED_GREEN);
}else{
LED_Off(LED_GREEN);
}
}else if(!strcmp(led,”blue”)){
if(led_value){
LED_On(LED_BLUE);
}else{
LED_Off(LED_BLUE);
}
}else if(!strcmp(led,”yellow”)){
if(led_value){
LED_On(LED_YELLOW);
printf(“Yellow On\n\r”);
}else{
LED_Off(LED_YELLOW);
printf(“Yellow Off\n\r”);
}
}
// 釋放記憶體空間
cJSON_Delete(root);
}else{
printf(“Error before: [%s]\n\r”,cJSON_GetErrorPtr());
}
}
delay_ms(500);
//獲取感測器測量資料,該示例使用隨機數
temperature = rand()%50;
humidity = rand()%100;
light = rand()%1000;
pressure = rand()%1000;
//將資料合成為JSON格式資料
sprintf(meassage,”{\”temperature\”:%.1f,\”humidity\”:%.1f,\”light\”:%.1f,\”pressure\”:%.1f}”,temperature,humidity,light,pressure);
//將資料傳送出去
mqtt_publish(“pyboard_value”,meassage);
}
}
完整工程程式碼可在後面的附件下載。
2
手機端程式碼實現
手機端我們也使用官方提供的Java庫Java client and utilities,下載地址:
http://www.eclipse.org/paho/
將jar檔案新增到工程中即可,程式介面如下所示:
上面4個條目分別顯示STM32微控制器通過W5500傳送到伺服器端的感測器測量資料;
下面4個圖片分別控制板子上的4個LED燈;
訊息傳送我們採用執行緒的方式傳送,接收採用回撥函式方式接收訊息。
2.1
實現訊息傳送
傳送訊息的程式碼如下所示:
/**
* send message
*/
class PublishThread extends Thread {
String topic;
MqttMessage message;
int qos = 0;
MemoryPersistence persistence = new MemoryPersistence();
PublishThread(String topic,String message){
this.topic = topic;
this.message = new MqttMessage(message.getBytes());
}
public void sendMessage(String topic,String message){
this.topic = topic;
this.message = new MqttMessage(message.getBytes());
run();
}
@Override
public void run() {
try {
MqttClient sampleClient = new MqttClient(broker, clientId, persistence);
MqttConnectOptions connOpts = new MqttConnectOptions();
connOpts.setCleanSession(true);
connOpts.setKeepAliveInterval(1);
System.out.println(“Connecting to broker: ” + broker);
sampleClient.connect(connOpts);
System.out.println(“Connected”);
System.out.println(“Publishing message: ” + message.toString());
message.setQos(qos);
sampleClient.publish(topic, message);
System.out.println(“Message published”);
sampleClient.disconnect();
System.out.println(“Disconnected”);
}catch(MqttException me) {
System.out.println(“reason “+me.getReasonCode());
System.out.println(“msg “+me.getMessage());
System.out.println(“loc “+me.getLocalizedMessage());
System.out.println(“cause “+me.getCause());
System.out.println(“excep “+me);
me.printStackTrace();
}
}
}
2.2
實現訊息接收
接收訊息的程式碼如下所示:
/**
* receive message
*/
class SubscribeThread extends Thread{
final String topic;
MemoryPersistence persistence = new MemoryPersistence();
SubscribeThread(String topic){
this.topic = topic;
}
@Override
public void run(){
try {
final MqttClient sampleClient = new MqttClient(broker, clientId, persistence);
final MqttConnectOptions connOpts = new MqttConnectOptions();
connOpts.setCleanSession(true);
System.out.println(“Connecting to broker: ” + broker);
connOpts.setKeepAliveInterval(5);
sampleClient.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable throwable) {
System.out.println(“connectionLost”);
try {
sampleClient.connect(connOpts);
sampleClient.subscribe(topic);
}catch (MqttException e){
e.printStackTrace();
}
}
@Override
public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
System.out.println(“messageArrived:”+mqttMessage.toString());
System.out.println(topic);
System.out.println(mqttMessage.toString());
try {
JSONTokener jsonParser = new JSONTokener(mqttMessage.toString());
JSONObject person = (JSONObject) jsonParser.nextValue();
temperature = person.getDouble(“temperature”);
humidity = person.getDouble(“humidity”);
light = person.getDouble(“light”);
pressure = person.getDouble(“pressure”);
System.out.println(“temperature = ” + temperature);
System.out.println(“humidity = ” + humidity);
runOnUiThread(new Runnable() {
@Override
public void run() {
temperatureTextView.setText(String.format(“%.1f”, temperature));
humidityTextView.setText(String.format(“%.1f”, humidity));
lightTextView.setText(String.format(“%.1f”, light));
pressureTextView.setText(String.format(“%.1f”, pressure));
}
});
} catch (JSONException ex) {
ex.printStackTrace();
}
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
System.out.println(“deliveryComplete”);
}
});
sampleClient.connect(connOpts);
sampleClient.subscribe(topic);
} catch(MqttException me) {
System.out.println(“reason “+me.getReasonCode());
System.out.println(“msg “+me.getMessage());
System.out.println(“loc “+me.getLocalizedMessage());
System.out.println(“cause “+me.getCause());
System.out.println(“excep “+me);
me.printStackTrace();
}
}
}
3
實測效果
1,微控制器端定時更新感測器資料,手機端也會同步更新;
2,手機端點選4個LED控制的按鈕,板子上也會點亮或者熄滅對應的LED;
4 原始碼下載
4.1
STM32端原始碼下載
MQTT_STM32_W5500.rar
4.2
手機端原始碼下載
MQTT_Android.rar
4.3
手機端apk下載
stm32_w5500_mqtt_app.rar
相關文章
- 串列埠轉發閘道器實現工業裝置資料採集和遠端控制串列埠
- 基於PLC的礦井通風控制系統如何實現資料採集和遠端監控?
- 物聯網終端裝置——遠端資料採集儀
- 西門子PLC水處理系統如何實現資料採集遠端監控?
- 工業智慧閘道器實現工業機器人資料採集遠端監控機器人
- 透過Lora無線閘道器實現資料採集遠端監控究竟有多香?
- 物聯網閘道器採集工業裝置資料實現遠端監控管理
- 淋膜機PLC資料採集實現手機端遠端監控預警系統解決方案
- 《遠端控制》-服務端實現(一)服務端
- PLC實時資料採集如何實現?
- 基於資料採集閘道器實現工業生產現場裝置遠端監控系統
- 英特佩斯遠端資料採集和車隊管理平臺
- 資料採集行業現狀及解決方案行業
- Go實現ssh執行遠端命令及遠端終端Go
- 數控磨床資料採集遠端監控物聯網系統
- SSH遠端訪問及控制
- 遠端訪問及控制——ssh
- 透過工業物聯網平臺實現靜電紡絲機的資料採集遠端監控
- 資料採集實驗四
- 資料採集標註行業現狀及解決方案行業
- 水質監測儀資料採集遠端監控系統解決方案
- 消防應急電源資料採集遠端監控系統解決方案
- 熱壓機PLC資料採集遠端監控物聯網解決方案
- PHP 實現簡單的資料採集併入庫PHP
- 如何實現車聯網的靈活資料採集
- 自動繞線機PLC資料採集遠端維護上下載系統方案
- ViCANdo — 智慧駕駛資料採集及資料分析平臺
- 資料採集實踐作業2
- Linux 實現本、異地遠端資料實時同步功能Linux
- 01 . Go語言實現SSH遠端終端及WebSocketGoWeb
- 快速實現無人車遠端控制開發——實踐類
- VNC遠端控制,VNC遠端控制連線WindowsVNCWindows
- 鋰電池塗布機資料採集遠端監控物聯網解決方案
- 智慧養殖自動化PLC資料採集遠端監控系統解決方案
- 汙水處理自動化裝置資料採集與遠端監控解決方案
- 快速實現本地資料備份與FTP遠端資料遷移FTP
- 物聯網全連線工廠:實現生產過程資料採集及智慧管理
- 工業自動化控制系統中的PLC模擬量訊號資料採集如何實現?
- Oracle資料倉儲的實時資料採集XSOracle