Vilket MODBUS bibliotek använder du?

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46885
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Vilket MODBUS bibliotek använder du?

Inlägg av TomasL »

Visst
Funktionen interfacar mellan huvudapplikationen och MB-biblioteket
Funktionen ligger i MB Master och skickar data till slaven

Kod: Markera allt

// use only by modbus MASTER
//! MB_send (c_com_data_struct *mb_data)
//! mb_data = pekare till en c_com_data_struct.
//! skickar ett SKRIV kommando och skriver ETT(1) register
void MB_send (c_com_data_struct *mb_data, uint16 opt) {
	sint16 retry = 1;
	sint32 iii;
	sint16 w;

	register uint16 MB_X_u16;

	w = mb_com_wait_arr[global_inst.MB_baud_s16];

	MB_RWdata.addr 	= mb_data->addr;
	MB_RWdata.reg 		= mb_data->reg;
	MB_RWdata.data[0] = mb_data->data[0];
	MB_RWdata.data[1] = mb_data->data[1];
	
	vTaskDelay(3);
	
	while(retry > 0) {
		mb_data->prio  = 1;
		mb_data->error = 0;
		mb_data->ready = 0;
		mb_data->regNum = 1;
		
		mb_data->addr = MB_RWdata.addr;
		mb_data->reg = MB_RWdata.reg;
		mb_data->data[0] = MB_RWdata.data[0];
		mb_data->data[1] = MB_RWdata.data[1];
		
		mb_data->fn = 6;	// 6 = write single register
		if( opt & MB_OPTD_COIL  ) {
			mb_data->fn = 5; // 5 = write Coil 
			if(mb_data->data[0] != 0) {
				mb_data->data[0] = 0xff00; // only true value
			}
		}
		if( opt & MB_OPTD_BITS_32  ) 
		{
			mb_data->fn = 16; // 16 = write multiple registers
			mb_data->regNum = 2;	
	//		mb_data->data[2] = MB_RWdata.data[2];
	//		mb_data->data[3] = MB_RWdata.data[3];
		}
		
//		Print_adio("MBSEND78");
		UpdateCmdMBQueue( mb_data );

		vTaskDelay(w); // master-->slave...slave-->master, Wait for answer

		iii = MBCOM_TIMEOUT_MS;
		while(mb_data->ready == 0) {
			iii--;
			if(iii == 0) break;
			vTaskDelay(2);		//	Wait,a little more, for answer
		}


		MB_frames++;
		if(mb_data->error == 0) {
			retry = 0;			
		}
		else {
			MB_errors++;
			MB_err++;
			MB_X_u16 = 0;
			if(MB_EX_ILLEGAL_FUNCTION 		== mb_data->error) MB_X_u16 = 1;
			if(MB_EX_ILLEGAL_DATA_ADDRESS == mb_data->error) MB_X_u16 = 1;
			if(MB_EX_ILLEGAL_DATA_VALUE  	== mb_data->error) MB_X_u16 = 1;
			if(MB_EX_SLAVE_DEVICE_FAILURE == mb_data->error) MB_X_u16 = 1;
			if(MB_X_u16) {
				logg_X_Write(B_LARM, B_LARM_MODBUS, mb_data->error, mb_data->addr);				
			}	
		}
		
		if( MB_err > 100) {
	//		logg_X_Write(A_LARM, A_LARM_MODBUS, mb_data->error, mb_data->addr);
			MB_err = 0;
		}	
		if(MB_frames > 1000) {
			MB_frames = 0;
			if(MB_err > 0) MB_err--;
		}			

		retry --;
	}
}
//---------------------------------------------------------
hawkan
Inlägg: 3400
Blev medlem: 14 augusti 2011, 10:27:40

Re: Vilket MODBUS bibliotek använder du?

Inlägg av hawkan »

Tittade lite på dokumentationen för den Modbus-kod som TomasL förespråkar. Det är säkert bra.
Men det är inget för mej. Det har begränsat stöd för hårdvara och inget stöd för de mellanlager jag använder.
Noterbart STM-familjen har inget stöd, inte heller esp32 eller esp8266, där jag brukar jobba.
Som sagt, säkert bra. Men för min del hoppar jag över det. Jag har i alla fall tittat på det, mer än jag gjort innan.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46885
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Vilket MODBUS bibliotek använder du?

Inlägg av TomasL »

Hawkan, vad har du fått det ifrån?
Supported platforms

Embedded platforms with no operating systems (AVR, MSP430, PIC32, ARM Cortex (Atmel, Freescale, ST, NXP and others), 8051 and others
Embedded platforms with RTOS (FreeRTOS, ucOS-II, Keil RTX and others
Operating systems (Windows, Linux and others)
hawkan
Inlägg: 3400
Blev medlem: 14 augusti 2011, 10:27:40

Re: Vilket MODBUS bibliotek använder du?

Inlägg av hawkan »

Den här listan. Finns ingen port för de jag nämnde.
https://www.embedded-experts.at/en/free ... uirements/
DanielM
Inlägg: 2421
Blev medlem: 5 september 2019, 14:19:58

Re: Vilket MODBUS bibliotek använder du?

Inlägg av DanielM »

TomasL skrev: 1 januari 2025, 15:22:37 Visst
Funktionen interfacar mellan huvudapplikationen och MB-biblioteket
Funktionen ligger i MB Master och skickar data till slaven

Kod: Markera allt

// use only by modbus MASTER
//! MB_send (c_com_data_struct *mb_data)
//! mb_data = pekare till en c_com_data_struct.
//! skickar ett SKRIV kommando och skriver ETT(1) register
void MB_send (c_com_data_struct *mb_data, uint16 opt) {
	sint16 retry = 1;
	sint32 iii;
	sint16 w;

	register uint16 MB_X_u16;

	w = mb_com_wait_arr[global_inst.MB_baud_s16];

	MB_RWdata.addr 	= mb_data->addr;
	MB_RWdata.reg 		= mb_data->reg;
	MB_RWdata.data[0] = mb_data->data[0];
	MB_RWdata.data[1] = mb_data->data[1];
	
	vTaskDelay(3);
	
	while(retry > 0) {
		mb_data->prio  = 1;
		mb_data->error = 0;
		mb_data->ready = 0;
		mb_data->regNum = 1;
		
		mb_data->addr = MB_RWdata.addr;
		mb_data->reg = MB_RWdata.reg;
		mb_data->data[0] = MB_RWdata.data[0];
		mb_data->data[1] = MB_RWdata.data[1];
		
		mb_data->fn = 6;	// 6 = write single register
		if( opt & MB_OPTD_COIL  ) {
			mb_data->fn = 5; // 5 = write Coil 
			if(mb_data->data[0] != 0) {
				mb_data->data[0] = 0xff00; // only true value
			}
		}
		if( opt & MB_OPTD_BITS_32  ) 
		{
			mb_data->fn = 16; // 16 = write multiple registers
			mb_data->regNum = 2;	
	//		mb_data->data[2] = MB_RWdata.data[2];
	//		mb_data->data[3] = MB_RWdata.data[3];
		}
		
//		Print_adio("MBSEND78");
		UpdateCmdMBQueue( mb_data );

		vTaskDelay(w); // master-->slave...slave-->master, Wait for answer

		iii = MBCOM_TIMEOUT_MS;
		while(mb_data->ready == 0) {
			iii--;
			if(iii == 0) break;
			vTaskDelay(2);		//	Wait,a little more, for answer
		}


		MB_frames++;
		if(mb_data->error == 0) {
			retry = 0;			
		}
		else {
			MB_errors++;
			MB_err++;
			MB_X_u16 = 0;
			if(MB_EX_ILLEGAL_FUNCTION 		== mb_data->error) MB_X_u16 = 1;
			if(MB_EX_ILLEGAL_DATA_ADDRESS == mb_data->error) MB_X_u16 = 1;
			if(MB_EX_ILLEGAL_DATA_VALUE  	== mb_data->error) MB_X_u16 = 1;
			if(MB_EX_SLAVE_DEVICE_FAILURE == mb_data->error) MB_X_u16 = 1;
			if(MB_X_u16) {
				logg_X_Write(B_LARM, B_LARM_MODBUS, mb_data->error, mb_data->addr);				
			}	
		}
		
		if( MB_err > 100) {
	//		logg_X_Write(A_LARM, A_LARM_MODBUS, mb_data->error, mb_data->addr);
			MB_err = 0;
		}	
		if(MB_frames > 1000) {
			MB_frames = 0;
			if(MB_err > 0) MB_err--;
		}			

		retry --;
	}
}

//---------------------------------------------------------
Rätt grötig kod det där. Är du verkligen säker på att man ska skriva så och ge sig själv en medalj?
Så här brukar jag skriva C kod.

Kod: Markera allt

/*
 * modbus.c
 *
 *  Created on: Dec 3, 2024
 *      Author: Daniel
 */

#include "modbusclient.h"
#include "modbusserver.h"

/* Handles */
static nmbs_t nmbs_server = {0};
static nmbs_t nmbs_client = {0};

/* Function pointers */
int32_t (*serial_read_function)(const char port[], uint8_t*, uint16_t, int32_t) = NULL;
int32_t (*serial_write_function)(const char port[], const uint8_t*, uint16_t, int32_t) = NULL;
char port_[20];

/* Read via serial */
int32_t read_serial(uint8_t* buf, uint16_t count, int32_t byte_timeout_ms, void* arg) {
	if (serial_read_function) {
		return serial_read_function(port_, buf, count, byte_timeout_ms);
	}
    return 0;
}

/* Write via serial */
int32_t write_serial(const uint8_t* buf, uint16_t count, int32_t byte_timeout_ms, void* arg) {
	if (serial_write_function) {
		return serial_write_function(port_, buf, count, byte_timeout_ms);
	}
    return 0;
}

void modbus_set_serial_write(int32_t (*serial_write)(const char port[], const uint8_t*, uint16_t, int32_t)){
	serial_write_function = serial_write;
}

void modbus_set_serial_read(int32_t (*serial_read)(const char port[], uint8_t*, uint16_t, int32_t)){
	serial_read_function = serial_read;
}

void modbus_set_serial_port(const char port[]) {
	strcpy(port_, port);
}

/* Server functions */
bool modbus_server_create_RTU(const uint8_t address) {

	/* Configuration */
	nmbs_platform_conf platform_conf;
	nmbs_platform_conf_create(&platform_conf);
	platform_conf.transport = NMBS_TRANSPORT_RTU;
	platform_conf.read = read_serial;
	platform_conf.write = write_serial;
	platform_conf.arg = NULL;

	/* Callbacks */
	nmbs_callbacks callbacks;
	nmbs_callbacks_create(&callbacks);
	callbacks.read_coils = handle_read_coils;
	callbacks.read_discrete_inputs = handle_read_discrete_inputs;
	callbacks.read_holding_registers = handle_read_holding_registers;
	callbacks.read_input_registers = handle_read_input_registers;
	callbacks.write_single_coil = handle_write_single_coil;
	callbacks.write_single_register = handle_write_single_register;
	callbacks.write_multiple_coils = handle_write_multiple_coils;
	callbacks.write_multiple_registers = handle_write_multiple_registers;
	callbacks.read_file_record = handle_read_file_record;
	callbacks.write_file_record = handle_write_file_record;
	callbacks.read_device_identification_map = handle_read_device_identification_map;
	callbacks.read_device_identification = handle_read_device_identification;

	/* Create the modbus server */
	nmbs_error err = nmbs_server_create(&nmbs_server, address, &platform_conf, &callbacks);
	if (err != NMBS_ERROR_NONE) {
		return false;
	}

	/* Timeouts */
	nmbs_set_read_timeout(&nmbs_server, 1000);
	nmbs_set_byte_timeout(&nmbs_server, 100);

	/* Set handle */
    modbus_set_server_handle(&nmbs_server);

	/* All went OK */
	return true;
}

bool modbus_server_polling(){
	return modbus_polling();
}

bool modbus_server_set_digital_outputs(const uint8_t outputs[], const uint16_t address, const uint16_t quantity){
	return modbus_set_digital_outputs_on_server(outputs, address, quantity);
}

bool modbus_server_set_digital_inputs(const uint8_t inputs[], const uint16_t address, const uint16_t quantity){
	return modbus_set_digital_inputs_on_server(inputs, address, quantity);
}

bool modbus_server_set_analog_inputs(const uint16_t inputs[], const uint16_t address, const uint16_t quantity){
	return modbus_set_analog_inputs_on_server(inputs, address, quantity);
}

bool modbus_server_get_parameters(uint16_t parameters[], const uint16_t address, const uint16_t quantity){
	return modbus_get_parameters_at_server(parameters, address, quantity);
}

bool modbus_server_set_parameters(const uint16_t parameters[], const uint16_t address, const uint16_t quantity){
	return modbus_set_parameters_on_server(parameters, address, quantity);
}

/* Client functions */
bool modbus_client_create_RTU(const uint8_t address) {

    /* Create platform configuration */
    nmbs_platform_conf platform_conf;
    nmbs_platform_conf_create(&platform_conf);
    platform_conf.transport = NMBS_TRANSPORT_RTU;
    platform_conf.read = read_serial;
    platform_conf.write = write_serial;
    platform_conf.arg = NULL;

    /* Create client */
    nmbs_error err = nmbs_client_create(&nmbs_client, &platform_conf);
    if (err != NMBS_ERROR_NONE) {
        return false;
    }

    /* Set time out */
    nmbs_set_read_timeout(&nmbs_client, 100);
    nmbs_set_byte_timeout(&nmbs_client, 100);

    /* Set address */
    nmbs_set_destination_rtu_address(&nmbs_client, address);

    /* Set handle */
    modbus_set_client_handle(&nmbs_client);

    /* OK */
    return true;
}

void modbus_client_set_RTU_address(const uint8_t address) {
	nmbs_set_destination_rtu_address(&nmbs_client, address);
}

bool modbus_client_get_digital_outputs(uint8_t outputs[], const uint16_t address, const uint16_t quantity){
	return modbus_get_digital_outputs_from_server(outputs, address, quantity);
}

bool modbus_client_get_digital_inputs(uint8_t inputs[], const uint16_t address, const uint16_t quantity){
	return modbus_get_digital_inputs_from_server(inputs, address, quantity);
}

bool modbus_client_get_analog_inputs(uint16_t inputs[], const uint16_t address, const uint16_t quantity){
	return modbus_get_analog_inputs_from_server(inputs, address, quantity);
}

bool modbus_client_set_parameters(const uint16_t parameters[], const uint16_t address, const uint16_t quantity){
	return modbus_set_parameters_to_server(parameters, address, quantity);
}

bool modbus_client_get_parameters(uint16_t parameters[], const uint16_t address, const uint16_t quantity){
	return modbus_get_parameters_from_server(parameters, address, quantity);
}
hawkan
Inlägg: 3400
Blev medlem: 14 augusti 2011, 10:27:40

Re: Vilket MODBUS bibliotek använder du?

Inlägg av hawkan »

Jaha såg just att det fanns två olika, Modbus och freemodbus.

Denna har nog varit uppe tidigare, men väl värt en läsning av kapitel 2, det är tolv sidor.
Modbus over Serial line. Det här med 3.5 char?
https://www.modbus.org/docs/Modbus_over ... _V1_02.pdf
Niklas-k
Inlägg: 354
Blev medlem: 10 mars 2004, 15:59:21
Ort: Katrineholm

Re: Vilket MODBUS bibliotek använder du?

Inlägg av Niklas-k »

Intressant ämne som jag vill också implementera i en mikrokontroller och hoppas denna tråd lever länge

Det där med 3.5 char
så har det med hastighet


Character-Time: the time it takes to transmit one character at the chosen baud rate. In RTU mode, there are 11 bits per character and this would be 11 bit-times. For example, at 9600 baud, 1 character-time is 11 bit times or 11bits/char* 1/9600bits/sec = 1146us/char.

saxat från:
https://www.automation.com/en-us/articl ... -to-modbus
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46885
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Vilket MODBUS bibliotek använder du?

Inlägg av TomasL »

För slaven är timing viktig, för att kunna skilja på saker och ting, samt att kunna skilja på korrekt RX och felaktig RX, samt naturligtvis att skicka med korrekta intervall
För mastern är timing viktigt, för dels skicka korrekt och dels för att särskilja på korrekt RX och fel RX.

Oavsett vilket bibliotek man använder är det faktiskt nödvändigt att veta hur MODBUS fungerar dvs lära sig det utan och innan.
hawkan
Inlägg: 3400
Blev medlem: 14 augusti 2011, 10:27:40

Re: Vilket MODBUS bibliotek använder du?

Inlägg av hawkan »

Om man använder ett färdigt modbus ska man inte behöva lära sej alla detaljer.
Därför man tar något färdigt. Annars kan man lika gärna göra det själv.
Och använder man det för sej själv, så spelar det ingen roll om det är "not in conformity"

11 bits, så här säger dokumentet
Skärmbild 2025-01-02 191749.png
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46885
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Vilket MODBUS bibliotek använder du?

Inlägg av TomasL »

Förvisso är det så, om Biblioteket är gjort i enlighet med standarden.
Frågan är om det bibliotek som TS använder är gjort enligt standard, det verkar dock inte så.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46885
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Vilket MODBUS bibliotek använder du?

Inlägg av TomasL »

Vi implementerade det bibliotek jag länkade till ihop med OpenRTOS på en PIC32MX795, det funkar perfekt utan några som helst modifikationer, från dag 1.
Dock fick vi lite problem med ABB omformare, som i vissa lägen skickar fel checksumma, så vi fick modda lite grand innan ABB fixade det hela.
I Mastern körde vi både MASTER RTU och SERVER TCP, i TCP-koden så använde vi DMA vid mottagning, eftersom det inte finns någon timing på TCP-versionen.
I RTU-koden så körde vi tighta interrupt för både mottagning och sändning, i hastigheter upp till runt 230kb/s(230400 b/s) över det fick vi problem. Eftersom vi defaultade till 19,2 så var det inga problem.
DanielM
Inlägg: 2421
Blev medlem: 5 september 2019, 14:19:58

Re: Vilket MODBUS bibliotek använder du?

Inlägg av DanielM »

TomasL skrev: 2 januari 2025, 20:08:52 Förvisso är det så, om Biblioteket är gjort i enlighet med standarden.
Frågan är om det bibliotek som TS använder är gjort enligt standard, det verkar dock inte så.
Nej. nanoMODBUS tar inte hänsyn till hårdvara eller timing. Det får användaren göra.
Det är därför nanoMODBUS går att implementera på en...tja...faktiskt en Commondore 64 om man har C-kompilatorn tillgänglig. :razz:
DanielM
Inlägg: 2421
Blev medlem: 5 september 2019, 14:19:58

Re: Vilket MODBUS bibliotek använder du?

Inlägg av DanielM »

Niklas-k skrev: 2 januari 2025, 18:59:29 Intressant ämne som jag vill också implementera i en mikrokontroller och hoppas denna tråd lever länge

Det där med 3.5 char
så har det med hastighet


Character-Time: the time it takes to transmit one character at the chosen baud rate. In RTU mode, there are 11 bits per character and this would be 11 bit-times. For example, at 9600 baud, 1 character-time is 11 bit times or 11bits/char* 1/9600bits/sec = 1146us/char.

saxat från:
https://www.automation.com/en-us/articl ... -to-modbus
Du kan testa tanka ner nanoMODBUS och implementera det. Jag har laddad upp ett bibliotek på föregående eller förr förra sidan. Superenkelt.
DanielM
Inlägg: 2421
Blev medlem: 5 september 2019, 14:19:58

Re: Vilket MODBUS bibliotek använder du?

Inlägg av DanielM »

TomasL skrev: 2 januari 2025, 20:21:32 Vi implementerade det bibliotek jag länkade till ihop med OpenRTOS på en PIC32MX795, det funkar perfekt utan några som helst modifikationer, från dag 1.
Dock fick vi lite problem med ABB omformare, som i vissa lägen skickar fel checksumma, så vi fick modda lite grand innan ABB fixade det hela.
I Mastern körde vi både MASTER RTU och SERVER TCP, i TCP-koden så använde vi DMA vid mottagning, eftersom det inte finns någon timing på TCP-versionen.
I RTU-koden så körde vi tighta interrupt för både mottagning och sändning, i hastigheter upp till runt 230kb/s(230400 b/s) över det fick vi problem. Eftersom vi defaultade till 19,2 så var det inga problem.

Om du ska ta emot X bytes, som du har ingen kännedom om. Hur skulle du göra då?

Du kan använda en interrupt som aktiveras vid 1 byte. Men om du skickar 10 byte, då kommer interrupten att köras 10 gånger, eller hinner du läsa 10 byte, innan du återkallar på interruptens inställning igen?
H.O
Inlägg: 5892
Blev medlem: 19 mars 2007, 10:11:27
Ort: Ronneby

Re: Vilket MODBUS bibliotek använder du?

Inlägg av H.O »

Det beror väl på hur stor UART'ens hårdvarubuffert är (?)
Om den, som på 8-bitars PIC är hela 1 byte (plus shiftregistret) så behöver man ju en interrupt per byte. Sen kan man ju, för säkerhets skull, kolla om det inkommit ytterligare en byte innan man hoppar ur - ifall det finns minsta chans till det.

Om du har en större buffert eller kan köra typ DMA så behövs ju inte det men då vet jag inte hur man (för MOSBUS RTU) löser timeout-grejen (3.5 char eller fast tid beroende på baudrate). Den timern behöver man ju starta om vid varje inkommet byte för att det skall funka. På mer avancerade uC så kanske det finns interna "kopplingar" man kan göra för att uppnå det - jag har nästan bara jobbat med 8-bitars PIC så jag har inte koll.
Skriv svar