/* Read values from modbus-register */ int modbus_read_value(modbus_t *ctx, int address, float *value) { uint16_t val[2]; /* Check if modbus is set up */ if(ctx == NULL) { log_entry(APP_NAME, "Modbus: Error, No Context"); printf("Modbus: Error, No Context..\n"); return -1; } /* Read data from modbus */ if(modbus_read_registers(ctx, address, 2, val) <= 0) { fprintf(stderr, "Modbus: Reading error: %s\n", modbus_strerror(errno)); return -1; } /* Convert value to float */ *value = modbus_get_float(val); if(value == NULL) { return ERROR ; } else { return SUCCESS; } }
int main(void) { /* ######################### Variables ######################### */ /* Variables for modbus */ modbus_t *ctx; int pollingInterval; uint16_t valueArray[2]; float floatValue; int no_register; uint8_t desiredBit; GenDataType_t dataType; MoBuRegType_t registerType; /* Variables for mosquitto */ struct mosquitto *mosq; char topic[BUFFER_SIZE]; char text[50]; /* Variables for time */ struct timespec last_read_time; struct timespec now; /* ######################### Initialize variables ######################### */ sprintf(topic, "/test"); /* Read values from configfile */ if(getStringFromFile_n(CONFIG_FILE_PATH, "mosquitto_topic", topic, BUFFER_SIZE) == -1) { log_entry(APP_NAME, "Error reading topic"); printf("Error reading topic..\n"); /* wait for new config */ wait_for_new_config(); return EXIT_FAILURE; } pollingInterval = getIntFromFile(CONFIG_FILE_PATH, "modbus_polling_Interval"); no_register = getIntFromFile(CONFIG_FILE_PATH, "modbus_register"); dataType = getIntFromFile(CONFIG_FILE_PATH, "modbus_data_type"); registerType = getIntFromFile(CONFIG_FILE_PATH, "modbus_register_type"); desiredBit = getIntFromFile(CONFIG_FILE_PATH, "modbus_desired_bit"); /* Check values of variables */ if(check_values(no_register, pollingInterval) == ERROR) { log_entry(APP_NAME, "Error: Invalid values"); /* wait for new config */ wait_for_new_config(); return EXIT_FAILURE; } /* ######################### Initialize communication ######################### */ /* Initialize Mosquitto */ printf(SPLIT_LINE); mosq = mosquitto_initialize(CONFIG_FILE_PATH); if(mosq == NULL) { log_entry(APP_NAME, "Error: Could not initialize mqtt connection"); /* wait for new config */ wait_for_new_config(); return EXIT_FAILURE; } /* Initialize Modbus */ printf(SPLIT_LINE); ctx = modbus_init(CONFIG_FILE_PATH); if(ctx == NULL) { log_entry(APP_NAME, "Error: Could not initialize modbus connection"); /* wait for new config */ wait_for_new_config(); return EXIT_FAILURE; } /* Initialize time */ clock_gettime(CLOCK_MONOTONIC, &last_read_time); /* ######################### Start application ######################### */ printf(SPLIT_LINE); log_entry(APP_NAME, "Start reading values.."); printf("\nStart reading values..\n\n"); /* Endless Loop */ while(1) { clock_gettime(CLOCK_MONOTONIC, &now); /* Check if polling time is over (multiply with 1000 to calculate seconds) */ if(diff(&last_read_time, &now) >= pollingInterval * 1000) { /* Read values from modbus */ if(read_modbus(ctx, 0, no_register, dataType, registerType, desiredBit, valueArray) == ERROR) { /* Set last read time */ clock_gettime(CLOCK_MONOTONIC, &last_read_time); continue; } switch(dataType) { case bitCoil: case sint16: case sint32: case uint16: case uint32: floatValue = modbus_get_float(valueArray); sprintf(text, "%.0f", floatValue); break; case float32: floatValue = modbus_get_float(valueArray); sprintf(text, "%.2f", floatValue); break; } /* Message for user */ printf("Value: %s\n", text); /* Publish value */ if(mosquitto_pub(mosq, topic, strlen(text), text) == SUCCESS) { log_entry(APP_NAME, "Published value successfully"); printf("Published value successfully.\n\n"); } else { log_entry(APP_NAME, "Error: Value could not be published."); printf("Error: Value could not be published.\n"); } /* Set last read time */ clock_gettime(CLOCK_MONOTONIC, &last_read_time); } /* taking care of processor and slow down the loop */ usleep(500); } /* We should never get here */ mosquitto_quit(mosq); modbus_quit(ctx); log_entry(APP_NAME, "Quit application"); return EXIT_SUCCESS; }
/****************************************************************************** * * * Function: zbx_modbus_read_registers * * * * Purpose: a main entry point for processing of an item * * * * Parameters: request - structure that contains item key and parameters * * request->key - item key without parameters * * request->nparam - number of parameters * * request->timeout - processing should not take longer than * * this number of seconds * * request->params[N-1] - pointers to item key parameters * * * * result - structure that will contain result * * * * Return value: SYSINFO_RET_FAIL - function failed, item will be marked * * as not supported by zabbix * * SYSINFO_RET_OK - success * * * * Comment: get_rparam(request, N-1) can be used to get a pointer to the Nth * * parameter starting from 0 (first parameter). Make sure it exists * * by checking value of request->nparam. * * * ******************************************************************************/ int zbx_modbus_read_registers(AGENT_REQUEST *request, AGENT_RESULT *result) { char *param1, *param2,*param3,*param4,*param5,*param6,*param7; if (request->nparam <4) //check if mandatory params are provided { SET_MSG_RESULT(result, strdup("Invalid number of parameters.")); return SYSINFO_RET_FAIL; } param1 = get_rparam(request, 0); if(param_is_empty(param1)) { SET_MSG_RESULT(result, strdup("No connection address provided.")); return SYSINFO_RET_FAIL; } param2 = get_rparam(request, 1); if(param_is_empty(param2)) { SET_MSG_RESULT(result, strdup("No slave id provided.")); return SYSINFO_RET_FAIL; } param3 = get_rparam(request, 2); if(param_is_empty(param3)) { SET_MSG_RESULT(result, strdup("No register to read provided.")); return SYSINFO_RET_FAIL; } param4 = get_rparam(request, 3); if(param_is_empty(param4)) { SET_MSG_RESULT(result, strdup("No Modbus function provided! Please provide either 1,2,3,4.")); return SYSINFO_RET_FAIL; } modbus_t *ctx; int lock_required; create_modbus_context(param1,&ctx,&lock_required); if (ctx == NULL) { SET_MSG_RESULT(result, strdup("Unable to create the libmodbus context")); modbus_free(ctx); return SYSINFO_RET_FAIL; } //<slave_id> set slave id char *endptr; errno = 0; int slave_id = strtol(param2,&endptr, 0); if (errno!=0 || *endptr != '\0') { SET_MSG_RESULT(result, strdup("Check slaveid parameter")); modbus_free(ctx); return SYSINFO_RET_FAIL; } modbus_set_slave(ctx, slave_id); //<reg> set register to start from errno = 0; int reg_start = strtol(param3,&endptr, 0); if (errno!=0 || *endptr != '\0') { SET_MSG_RESULT(result, strdup("Check register to read")); modbus_free(ctx); return SYSINFO_RET_FAIL; } //set function to use errno = 0; int function = strtol(param4,&endptr, 0); if (errno!=0 || *endptr != '\0') { SET_MSG_RESULT(result, strdup("Check function (1,2,3,4) used")); modbus_free(ctx); return SYSINFO_RET_FAIL; } char datatype; int end = MODBUS_16BIT_LE; //<endianness> endianness LE(0) BE(1) default LE if (request->nparam > 4) { //optional params provided param5 = get_rparam(request, 4); //datatype if(!validate_datatype_param(param5)) { SET_MSG_RESULT(result, strdup("Check datatype provided.")); modbus_free(ctx); return SYSINFO_RET_FAIL; } datatype = *param5; // set datatype param6 = get_rparam(request, 5); //16 endiannes if(param6) { //endianness to use errno = 0; end = strtol(param6,&endptr, 0); if ( (end != MODBUS_16BIT_LE && end != MODBUS_16BIT_BE) || (errno!=0 || *endptr != '\0') ) { SET_MSG_RESULT(result, strdup("Check endiannes used")); modbus_free(ctx); return SYSINFO_RET_FAIL; } } param7 = get_rparam(request, 6); //PDU if(param7) {//PDU <first reg> check //int first_reg=atoi(param7); errno = 0; int first_reg = strtol(param7,&endptr, 0); if ( (first_reg != MODBUS_PROTOCOL_ADDRESS_1 && first_reg != MODBUS_PDU_ADDRESS_0) || (errno!=0 || *endptr != '\0') ) { SET_MSG_RESULT(result, strdup("Check addressing scheme(PDU,PROTOCOL) used")); modbus_free(ctx); return SYSINFO_RET_FAIL; } if (first_reg == MODBUS_PROTOCOL_ADDRESS_1){ reg_start=reg_start-1; } } } else {//no datatype set, place defaults if (function==MODBUS_READ_COIL_1 || function == MODBUS_READ_DINPUTS_2) { datatype = MODBUS_BIT;//default } if (function==MODBUS_READ_H_REGISTERS_3 || function == MODBUS_READ_I_REGISTERS_4) { datatype = MODBUS_INTEGER ;//default } } /* 3.0.3 struct timeval response_timeout ; response_timeout.tv_sec = 0; response_timeout.tv_usec = 0; modbus_set_response_timeout(ctx, &response_timeout); */ //modbus_set_response_timeout(ctx, 10, 0); //read part uint16_t tab_reg[64];//temp vars uint8_t tab_reg_bits[64]; int regs_to_read = 1; if (datatype == MODBUS_FLOAT || datatype == MODBUS_LONG) { regs_to_read=2;} if (lock_required == 1 ) LOCK_SERIAL_PORT; if (modbus_connect(ctx) == -1) { SET_MSG_RESULT(result, strdup(modbus_strerror(errno))); modbus_free(ctx); if (lock_required == 1 ) UNLOCK_SERIAL_PORT; return SYSINFO_RET_FAIL; } int rc;//modbus return_code switch (function) { case MODBUS_READ_COIL_1: rc = modbus_read_bits(ctx, reg_start, regs_to_read, tab_reg_bits); break; case MODBUS_READ_DINPUTS_2: rc = modbus_read_input_bits(ctx, reg_start, regs_to_read, tab_reg_bits); break; case MODBUS_READ_H_REGISTERS_3: rc = modbus_read_registers(ctx, reg_start, regs_to_read, tab_reg); break; case MODBUS_READ_I_REGISTERS_4: rc = modbus_read_input_registers(ctx, reg_start, regs_to_read, tab_reg); break; default : SET_MSG_RESULT(result, strdup("Check function (1,2,3,4) used")); //close connection modbus_close(ctx); if (lock_required == 1 ) UNLOCK_SERIAL_PORT; modbus_free(ctx); return SYSINFO_RET_FAIL; break; } //close connection modbus_close(ctx); if (lock_required == 1 ) UNLOCK_SERIAL_PORT; modbus_free(ctx); if (rc == -1) { SET_MSG_RESULT(result, strdup(modbus_strerror(errno))); return SYSINFO_RET_FAIL; } //post-parsing uint16_t temp_arr[2]; //output based on datatype switch(datatype){ case MODBUS_BIT: SET_UI64_RESULT(result, tab_reg_bits[0]); break; case MODBUS_INTEGER: SET_UI64_RESULT(result, tab_reg[0]); break; case MODBUS_FLOAT: if (end == MODBUS_16BIT_LE) { temp_arr[0] = tab_reg[0]; temp_arr[1] = tab_reg[1]; } if (end == MODBUS_16BIT_BE) { temp_arr[0] = tab_reg[1]; temp_arr[1] = tab_reg[0]; } SET_DBL_RESULT(result, modbus_get_float(temp_arr)); break; case MODBUS_LONG: //MODBUS_GET_INT32_FROM_INT16 is doing BIG_ENDIAN for register pair, so inverse registers (sort of hack) if (end == MODBUS_16BIT_LE) { temp_arr[0] = tab_reg[1]; temp_arr[1] = tab_reg[0]; } if (end == MODBUS_16BIT_BE) { temp_arr[0] = tab_reg[0]; temp_arr[1] = tab_reg[1]; } SET_UI64_RESULT(result, MODBUS_GET_INT32_FROM_INT16(temp_arr, 0)); break; default : SET_MSG_RESULT(result, strdup("Check datatype provided.")); modbus_free(ctx); return SYSINFO_RET_FAIL; break; } return SYSINFO_RET_OK; }