Paho-MQTT + GSM Modem + Stm32

Tatmin edilmemiş bir merak; işte ölümü bu kadar zor yapan şey bu.” – Beryl Markham, West with the Night

Merhabalar, umarım sizler ve aileleriniz güvendelerdir diyerek içeriğimize başlayabiliriz. Bugün GSM modem üzerinden internete çıkacağız ve Paho-Mqtt kütüphanesini projemize giydirip adafruit firmasının mqtt-brokerina yayın yapacağız.

MQTT protokolü ile teknik detaylar için internet üzerinden bulabileceğiniz teknik dökümanlar mevcut. Onlara göz gezdirmenizde bir fayda var.

Kullandığım GSM modem Quectel firmasının U95 modeli. Kendi gsm modem modelinize göre TCP/IP At komutlarının bulunduğu manuele göre revizeler gerçekleştirmeniz gerekebilir.

Öncelikle gömülü sistemler için kullanılan repoya şuradan erişebilirsiniz.

https://github.com/eclipse/paho.mqtt.embedded-c

Açıklama kısmında da belirtildiği gibi 3 alt projeye sahip bir repo. Bizim kullanacağımız dosyalar bu dizinde yer almakta.

https://github.com/eclipse/paho.mqtt.embedded-c/tree/master/MQTTPacket

Kaynak dosyalarını çalışacağınız ortama ekleyin.

Bir demo projesi olarak hazırladığım için kodlar biraz kirli ve mantıksal açıdan güzel olmadığını düşünüyorum. Tek amacım mqtt-brokere bağlanmak ve yayın yapmak. Yayın yaptığım süreç ya da gsm modeminin yayına çıkacağı işleyişte herhangi bir kontrol bulunmamakta. Bu yüzden üretim kodunuzda bu yapıyı kullanmanızı tavsiye etmem.

Kullandığım modemin networke açılmak, tcp üzerinden bir bağlantı sağlamak ve sağlanan bağlantı sonrasında veri gönderme işlemi için asgari kısım, modemin kendi manuelinde belirtilmiş.

Bu süreci gerçekleştirmek için biraz kod yazalım.

#ifndef API_GSM_H
#define API_GSM_H

#include "MQTTPacket.h"
#include "stdint.h"

#define MAX_NUM_OF_BUFFER 150

#define MQTT_DEBUG

#if defined MQTT_DEBUG
#define MQTT_LOG(...)	do{ printf(__VA_ARGS__); }while(0);
#else
#define MQTT_LOG(...) do{ }while(0);
#endif


typedef uint8_t gsm_result;

typedef struct _gsm_uart
{
	char data;
	char buffer[MAX_NUM_OF_BUFFER];
	uint8_t buffer_index;
	
}tgsm_uart;

extern tgsm_uart gsm_uart;

gsm_result 
send_cmd(char *cmd, char *response, uint16_t delay);

gsm_result 
mqtt_con(char *username, char* pass, char* id, unsigned short keep_alive);

gsm_result
mqtt_publish(char* topic, char* data);

gsm_result
send_data(char* payload, int data_len);


#endif
#include "API_GSM.h"
#include "usart.h"
#include "string.h"

tgsm_uart gsm_uart;

static void clear_gsm_uart(tgsm_uart *self)
{
	self -> buffer_index = 0;
	memset(self -> buffer, 0, sizeof(self -> buffer));
}

gsm_result 
send_cmd(char *cmd, char *response, uint16_t delay)
{
	clear_gsm_uart(&gsm_uart);
	HAL_UART_Transmit_IT(&huart6, (unsigned char*)cmd, strlen(cmd));
	
	HAL_Delay(delay);
	
	if(strstr(gsm_uart.buffer, response) != NULL)
	{
		printf("%s -> Okkey \r\n", cmd);
		return 0;
	}
	
	MQTT_LOG("%s -> False \r\n", cmd);
	
	return 1;
}

gsm_result 
mqtt_con(char *username, char* pass, char* id, unsigned short keep_alive)
{
	int ret = 0;
	unsigned char buf[128] = {0};
	MQTTPacket_connectData mqtt_packet = MQTTPacket_connectData_initializer;
	
	mqtt_packet.username.cstring = username;
	mqtt_packet.password.cstring = pass;
	mqtt_packet.clientID.cstring = id;
	mqtt_packet.keepAliveInterval = keep_alive;
	mqtt_packet.cleansession = 1;
	
	int len = MQTTSerialize_connect(buf, sizeof(buf), &mqtt_packet);
	ret  = send_data((char*)buf, len);
	
	return ret;
	
}

gsm_result
mqtt_publish(char* topic, char* data)
{
	int ret = 0;
	clear_gsm_uart(&gsm_uart);
    unsigned char buf[256] = {0};

    MQTTString topicString = MQTTString_initializer;
    topicString.cstring = topic;

    int mqtt_len = MQTTSerialize_publish(buf, sizeof(buf), 0, 0, 0, 0, topicString, (unsigned char*) data, strlen(data));
	ret = send_data((char*)buf, mqtt_len);
	
	return ret;

}

gsm_result
send_data(char* payload, const int data_len)
{
	int ret = 0;
	char str[128] = {0};
	int str_index = 0;
	
	snprintf(str, 128, "AT+QISEND=2,%d\r\n", data_len);
	
	send_cmd(str, ">", 1000);
	
	clear_gsm_uart(&gsm_uart);
	HAL_UART_Transmit_IT(&huart6, (unsigned char*)payload, data_len);
	
	HAL_Delay(1000);
	
	for(int i = data_len + 2; gsm_uart.buffer[i] != '\r'; ++i, str_index++)
	{
		str[str_index] = gsm_uart.buffer[i];
	}
	
	if(strstr(str, "SEND OK") != NULL)
	{
		MQTT_LOG("PAYLOAD SENDED SUCCESFULLY \r\n");
	}
	
	else
	{
		MQTT_LOG("FAILED \r\n")
		ret = 1;
	}
	
	return ret;
}
void HAL_UART_RxCpltCallback (UART_HandleTypeDef * huart) 
{
    if (huart == &huart6) 
	{
		HAL_UART_Receive_IT(&huart6 ,(uint8_t *)&gsm_uart.data, 1);
		gsm_uart.buffer[gsm_uart.buffer_index++] = gsm_uart.data;
	}
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
	/* USER CODE BEGIN 1 */

	/* USER CODE END 1 */

	/* MCU Configuration--------------------------------------------------------*/

	/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
	HAL_Init();

	/* USER CODE BEGIN Init */

	/* USER CODE END Init */

	/* Configure the system clock */
	SystemClock_Config();

	/* USER CODE BEGIN SysInit */

	/* USER CODE END SysInit */

	/* Initialize all configured peripherals */
	MX_GPIO_Init();
	MX_USART6_UART_Init();
	HAL_UART_Receive_IT(&huart6, (uint8_t *)&gsm_uart.data, 1);
	
	MQTT_LOG("GSM POWER ON, PROCESS WILL BE START IN FIVE MINUTES\r\n");
	/* USER CODE BEGIN 2 */
	char str[128] = {0};
	HAL_Delay(5000);
	
	send_cmd("AT\r\n", "OK\r\n", 5000);
	
	//send_cmd("ATI\r\n", "OK\r\n", 300);
	
	//send_cmd("AT+CPIN?\r\n", "READY\r\n", 1000);
	
	//send_cmd("AT+CREG?\r\n", "0,1", 1000);
	
	send_cmd("AT+CGDCONT=1,\"ip\",\"mgbs\"\r\n", "OK\r\n", 1000);
	
	//send_cmd("AT+CGPADDR=1\r\n", "OK\r\n", 2000);
	
	snprintf(str, sizeof(str), "AT+QICSGP=1,1,\"%s\",\"%s\",\"%s\",1\r\n", "mgbs", "", "");
	send_cmd(str, "OK\r\n", 1000);
	memset(str, 0, sizeof(str));
	
	send_cmd("AT+QIACT=1\r\n", "OK\r\n", 4000);
	
	send_cmd("AT+QIOPEN=2,2,\"TCP\",\"io.adafruit.com\",1883,150,0\r\n", "OK\r\n", 4000);
	
	mqtt_con("embedded4ever", "adafruiot io key", "adafruiot io key", 60);
	
	/* USER CODE END 2 */

	/* Infinite loop */
	/* USER CODE BEGIN WHILE */
	while (1)
	{
	/* USER CODE END WHILE */
		char payload[10];
		static int i = 0;
		i++;
		snprintf(payload, 10, "%d", i);
		HAL_Delay(5000);
		mqtt_publish("embedded4ever/feeds/random", payload);
		
	/* USER CODE BEGIN 3 */
	}
  /* USER CODE END 3 */
}

Yukarıda bulunan kodda önemli bir kısım olan yer

mqtt_con("embedded4ever", "adafruiot io key", "adafruiot io key", 60);

Bu fonksiyon parametrelerinden ilk 3’ünü kendinize göre ayarlamanız gerekiyor.

İlk parametremiz, Adafruiot üyeliğinizde kullandığınız kullanıcı adı.

İkinci parametremiz, Adafruiot üyeliğinizin sonrasında almış olduğunuz io key.

Üçüncü parametremiz, client id olarak geçmekte. Bu parametre her kullanıcı için unique bir parametre olması gerekiyor. Bunun için dilerseniz internet üzerinde bulunan md5 hash generator sitelerini kullanabilirsiniz ya da direkt olarak halihazırda unique olarak size verilmiş io key’i kullanabilirsiniz. Burada şöyle bir tecrübe yaşadım ben bu parametreyi “1” olarak geçmiştim. Ve sistem ikinci yayınında düşüyordu. Bunun sebebi ise bir başka client id üzerinden yayın yapılırsa siz sistemden düşebiliyorsunuz. Tabi bunu okuduğumda saat gece 1’e geliyordu ve aydınlandım.

4.parametremiz keepalive parametresi, eğer yayıncı tarafından belirtilen bu süreç içerisinde broker’a bir veri aktarması gerçekleşmez ise, client taraf ping göndererek, broker tarafının bağlantıyı kapatmamasını sağlar.

Şimdi gelelim broker ile ilgili ayarlara. Öncelikle https://www.adafruit.com/ sitesinden bir adet hesap açmanız gerekiyor. Hesabı açtiktan sonra sitenin arayüzünde bulunan header kısmında IO sekmesine geçiş yapalım.

Geçiş yaptıktan sonra böyle bir sayfa gelecektir. Bu sayfada sağ üst tarafta bulunan Adafruit IO Key‘e tıklayarak kullanıcı adı ve unique key’inizi görebilirsiniz.

Artık yayın yapacağımız konu(topic) girelim. Bunun için açılan sayfada Feed -> Action -> Create a New Feed diyerek belirlediğiniz topic adını girebilirsiniz.

Bu topic’e gelen verileri görselleştirmek isterseniz Dashboard seçeneğinden ilgili topic’e arayüzler ile eşleştirebilirsiniz.

Dikkat etmeniz gereken bir diğer nokta ise

mqtt_publish("embedded4ever/feeds/random", payload);

topic /feed/random olarak fonksiyona geçirilmemeli. Bu durumda gönderilen veri broker tarafından kabul edilmeyecektir. Topic yukarda belirtildiği gibi kullanıcı adını içermelidir.

Dashboard ekranından bir görüntü.

Bu konuda Türkçe kaynak eksikliği olduğu için yaptığım bir demoyu sizlerle paylaşmak istedim. Umarım faydalı olmuştur. Ek olarak sizlere güzel bir TEDx konuşması hediye etmek isterim.

5 comments

  1. Merhabalar hocam API_GSM.h dosyasına nasıl ulaşabilirim. Ayrıca hangi derleyeciyi kullanıyorsunuz aklımda bazı soru işaretleri varda merak ettim iyi günler dilerim.

    Beğen

  2. M66 modülü için gerekli ayar sırası aşağıdaki gibi Volkan hocam eline sağlık güzel bir yazı olmuş.

    send_cmd(“AT\r\n”, “OK\r\n”, 5000);

    send_cmd(“ATI\r\n”, “OK\r\n”, 300);

    send_cmd(“AT+QIMUX=?\r\n”, “OK\r\n”, 2000);

    send_cmd(“AT+CSQ\r\n”, “OK\r\n”, 300);

    send_cmd(“AT+CGACT=1,1\r\n”, “OK\r\n”, 4000);

    send_cmd(“AT+CGPADDR=1\r\n”, “OK\r\n”, 2000);

    send_cmd(“AT\r\n”, “OK\r\n”, 2000);

    send_cmd(“AT+CGATT=1\r\n”, “OK\r\n”, 4000);

    send_cmd(“AT+QIDNSIP=1\r\n”, “OK\r\n”, 4000);

    send_cmd(“AT+QICSGP=1,\”internet\”,\”\”,\”\”,\r\n”, “OK\r\n”, 4000);

    send_cmd(“AT+QIREGAPP?\r\n”, “OK\r\n”, 4000);

    send_cmd(“AT+QIOPEN=\”TCP\”,\”io.adafruit.com\”,1883\r\n”, “OK\r\n”, 6000);

    Beğen

    1. Elinize sağlık hocam.
      Ek olarak, şayet bu tarz bir init serisi kullanan arkadaşlar, sistemde daha sonrasında ip üzerinden bir bağlantı sağlayacaksa, DNSIP komutu 0’a çekilmeli.

      Beğen

Bir Cevap Yazın

Please log in using one of these methods to post your comment:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Google fotoğrafı

Google hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Connecting to %s