stm32 quectel modem

STM32 Uzaktan Gömülü Yazılım Güncelleme


Merhabalar, bu içeriği bootloader serisinin son yazısı olarak düşünebiliriz. Şayet direkt olarak bu yazıyla karşılaştıysanız ancak konu kümülatif bir şekilde ilerlediği için, şuradan diğer içerikleri incelemeniz sizin için faydalı olacaktır. Daha önce Duru isminde bir non-blocking, kullanımı rahat ve farklı MCU ailelerinden üyelerine port edilmesi mümkün ve rahat olan bir framework çalışmamı yayınlamıştım. Bugün ki uygulamamız yine o yapı üzerinde çalışacaktır.

Ne yapacağımızı daha rahat anlamak adına aşağıda bulunan görseli oluşturdum. Görsel Modemin (UC20 QUECTEL) ayağa kaldırılması gibi süreçleri içermiyor. Periyodik olarak backend tarafında yeni bir yazılım olup olmadığını sorguladığımız kısım özelinden başlıyor.

Belirlediğim formata göre sunucu üzerinde dosya şu şekilde barınmakta.

 can_%u.%u.%u.bin 
      |  | +---- bugfix-id
      |  + ----- minor-id
      + -------- major-id

Dosyanın hazırlanma sürecinde derleyici üzerinde yapılması gerekenlerden daha önce bahsetmiştim. Şimdi ise dosyanın içeriğinden bahsedeyim. ‘projeadı.bin’ dosyası oluştuktan sonra bu dosyayı Hex Editor programı üzerinden açarak dosyanın CRC32’sini buradan hesaplayarak ekliyorum ve ardından yayına verilecek versiyon damgasını 4 byte şeklinde ekliyorum.

Şimdi yazılım aşamasında Duru için gerekli yapıyı hazırlayalım, bunun için öncelikle ilgili callbackleri hazırlayalım. Ayrıca ilgili AT komutlarının kullandığınız modeme göre değişiklik gösterebileceğini unutmayınız.

/* İlgili sunucuya modem üzerinden ftp kanalı açılır */
uint8_t ftp_connect(void* self)
{	
	gsm_machine_debug("\r\n Host Connected !! \r\n");

	struct command_machine *cm = (struct command_machine*)self;
	
	char cr = {0x0D};
	
	char cmd[60] = { 0 };
	
	snprintf(cmd, sizeof(cmd), "AT+QFTPOPEN=\"%s\",%i%c","ftpservername", 21, cr);

	cm -> uart_tx_cb((uint8_t*)cmd, strlen(cmd));
	
	return true;
}
/* Sunucu üzerinde bulunan dosyalar listelenir, ilgili dosyanın olması durumunda yeni versiyon olup olmadığı flash üzerinde saklanılan adresten sorgulanır */
uint8_t parse_list_file (void* self, const char* buf, char* record_buf)
{
	if (self != NULL && strlen(buf) > 0)
	{
		char* ret = NULL;
		
		ret = strstr(buf, "CONNECT\r\n");
		
		if (ret != NULL)
		{
			ret = ret + sizeof("CONNECT\r\n");
			
			int i = 0;
			
			memset(gsm_uart.update_file_name, 0, sizeof(gsm_uart.update_file_name));
			
			while (*ret != '\r')
			{
					gsm_uart.update_file_name[i] = *ret++;
					i++;
			}

			ret = strstr(gsm_uart.update_file_name, "can_");
			
			if (ret != NULL)
			{
				ret = ret + sizeof("can");
				
				sscanf(ret, "%u.%u.%u", &gsm_uart.major, &gsm_uart.minor, &gsm_uart.bugfix);
				
				uint32_t current_version = *(uint32_t* )VERSION_ADDR;
				
				if (current_version == 0xFFFFFFFF)
				{
					is_any_new_firmware = true;
				}
				
				else
				{
					uint8_t major = current_version >> 24;
					uint8_t minor = current_version >> 16;
					uint8_t bug_fix = current_version >> 8;			
					
					char cv[10] = {0};
				
					snprintf(cv, sizeof(current_version), "%u.%u.%u", major, minor, bug_fix);
				
					int ret_version = comp_version(cv, ret);
					
					if (ret < 0)
					{
						is_any_new_firmware = true;
					}
					
					//Yeni update degil ise direkt olarak çik.
					else
					{
						struct command_machine* cm = (struct command_machine*)self;
						cm -> command_index = cm -> command_index + 3;
					}
				}
			}
		}	
	}
		
	return 0;
}
/* Dosya Modemin RAM bölgesine indirilir */
uint8_t ftp_get_file(void* self)
{
	
	gsm_machine_debug("\r\n Host Connected !! \r\n");

	struct command_machine *cm = (struct command_machine*)self;

	char cmd[60] = { 0 };
	
	snprintf(cmd, sizeof(cmd), "AT+QFTPGET=\"%s\",\"RAM:can.bin\"\r\n", gsm_uart.update_file_name);

	cm -> uart_tx_cb((uint8_t*)cmd, strlen(cmd));
	
	return true;
}
/* Dosyanın boyutu sunucudan istenilir */
uint8_t ftp_get_size(void* self)
{
	
	gsm_machine_debug("\r\n Host Connected !! \r\n");

	struct command_machine *cm = (struct command_machine*)self;
		
	char cmd[60] = { 0 };	
	
	snprintf(cmd, sizeof(cmd), "AT+QFTPSIZE=\"%s\"\r\n", gsm_uart.update_file_name);

	cm -> uart_tx_cb((uint8_t*)cmd, strlen(cmd));
	
	return true;
}
extern bool is_any_new_firmware;
extern float file_size;

/* Dosyanın boyutu ve dosya sorgulanır */
uint8_t ftp_file_size_cb(void* self, const char* buf, char* record_buf)
{
	if (self != NULL && strlen(buf) > 0)
	{
		char* ret = NULL;
		
		ret = strstr(buf, "OK\r\n\r\n");
		
		if (ret != NULL)
		{
			if (strstr(ret, "627") || strstr(ret, "550"))
			{
				is_any_new_firmware = false;
			}
			
			else 
			{
				ret = strstr(ret, "0,");
				
				if (ret)
				{
					ret = ret + 2;
					
					file_size = atof(ret);
					
					is_any_new_firmware = (file_size > 0) ? true : false;
				}
			}
		}
	}
	
	return 0;
}
/* Versiyonlar karşılaştırılır */
static int comp_version (const char* cv_version1, const char* version2 ) {
    unsigned major1 = 0, minor1 = 0, bugfix1 = 0;
	
    unsigned major2 = 0, minor2 = 0, bugfix2 = 0;
	
    sscanf(cv_version1, "%u.%u.%u", &major1, &minor1, &bugfix1);
	
    sscanf(version2, "%u.%u.%u", &major2, &minor2, &bugfix2);
	
    if (major1 < major2) return -1;
    if (major1 > major2) return 1;
    if (minor1 < minor2) return -1;
    if (minor1 > minor2) return 1;
    if (bugfix1 < bugfix2) return -1;
    if (bugfix1 > bugfix2) return 1;
    return 0;
}
/* Eğer herhangi bir güncelleme yok ise sorgulama durdulur */
int reset (void* self)
{
	if (self != NULL)
	{		
		struct command_machine* cm = (struct command_machine*)self;
		
		cm -> command_index = cm -> command_index + 3;
		
		//machine_stop_current(self);
	}
	
	return 0;	
}

Gelelim ilgili komutları ve komut pencerelerini hazırlamaya,

struct command ftp_open = {"FTP", "QFTPOPEN: 0,0\r\n", NULL, NULL, NULL, 2000, &ftp_connect };

struct command ftp_set_current_dir = {"AT+QFTPCWD=\"/\"\r\n", "QFTPCWD: 0,0", NULL, NULL, NULL, 3000, NULL };

struct command ftp_get_list_file_names = {"AT+QFTPNLST=\"/\",\"COM:\"\r\n", "+QFTPNLST:", NULL, NULL, &parse_list_file, 3000, NULL, reset};

struct command ftp_get_file_size = {"get_size", "QFTPSIZE:", NULL, NULL, &ftp_file_size_cb, 3000, &ftp_get_size };

struct command ftp_get_file_to_ram = {"get_file", "QFTPGET:", NULL, NULL, NULL, 3000, &ftp_get_file };

struct command ftp_close = {"AT+QFTPCLOSE\r\n", "+QFTPCLOSE: 0,0", NULL, NULL, NULL, 1500, NULL };

struct command ftp_deactivate_pdp = {"AT+QIDEACT=1\r\n", "OK\r\n", NULL, NULL, NULL, 4000, NULL };
const struct command* ftp_command_list_table[] = 
{
	&ftp_open,
	&ftp_set_current_dir,
	&ftp_get_list_file_names,
	&ftp_get_file_size,
	&ftp_get_file_to_ram,
	&ftp_close,
	&ftp_deactivate_pdp,	
};

Ve yeni bir güncelleme var ise artık dosyamızı sd karta yazdırabiliriz.

		if (is_any_new_firmware  && get_command_window_status(&command_machine_t, "FTP"))
		{
			// Herhangi bir yarım kalma durumları için dosya var olup olmaması sınanır.
			if ((fr = f_open(&file, "can.bin\0", FA_READ)) == FR_NO_FILE)
			{
					//Nothing.
			}
			
			else
			{
				f_close(&file);
				
				f_unlink("can.bin\0");
			}
			
			temp = file_size / 1000;
			
			temp_remainig = (file_size) - (temp * 1000);
		
			uart_tx_platform_specific((uint8_t *)"AT+QFOPEN=\"RAM:can.bin\",2\r\n", strlen("AT+QFOPEN=\"RAM:can.bin\",2\r\n"));
		
			HAL_Delay(500);
			
			while(temp-- > -1)
			{		
				int data_size = temp > -1 ? 1000 : temp_remainig;
			
				char temp_buf[75] = { 0 };
				
				snprintf(temp_buf, sizeof(temp_buf), "AT+QFREAD=3000,%d\r\n", 1000);
				
				clear_gsm_uart(&gsm_uart);	
				
				uart_tx_platform_specific((uint8_t *)temp_buf, strlen(temp_buf));
				
				HAL_Delay(1500);
			
				if (strlen(gsm_uart.buffer) > 0)
				{						
					char* ret = NULL;
					
					ret = strstr(gsm_uart.buffer, "CONNECT");
					
					if (ret != NULL)
					{
						ret = ret + sizeof("CONNECT");
						
						while (*ret++ != '\n');
						
						if ((fr = f_open(&file,(char *)"can.bin\0", FA_OPEN_APPEND | FA_WRITE)) == FR_OK)
						{												
							UINT bw;
							
							f_write(&file, ret, data_size, &bw);  
							
							fr = f_close(&file);	
						}						
					}
				}
			}
			
			NVIC_SystemReset();
		}

Aynı MCU içerisinde daha önce yazmış olduğum STM32 SD KART ÜZERİNDEN BOOT ETMEK içeriğinde bulunan yazılım her reset sonrasında çalıştırılmakta.

Eğer modeminiz destekliyorsa, ilgili dosyayı modemin RAM bölgesine indirilmesini sağlamak gerçekten güzel bir kolaylık sağlamakta. Buna ek olarak ilgili SSL sertifikalarını kullanarak rahatlıkla FTPS işlemini de yapabiliyorsunuz.

Sözün özü; Duru kütüphanesini kullanarak güncelleme yapmak bu kadar basit 🙂

Herkese iyi çalışmalar dilerim.