对参考项目mqtt2modbus 的分析

参考Github项目:
mqtt2modbus

使用make编译时出现了

main.c:11:10: fatal error: modbus.h: 没有那个文件或目录

解决办法:
fatal error: modbus/modbus.h: 没有那个文件或目录

编译之后出现了新错误:

main.c:12:10: fatal error: mosquitto.h: 没有那个文件或目录

解决办法:MQTT简介和mosquitto安装与使用及利用mosquitto库Linux c编程

这个项目中,对于Linux C结构体类型数据的定义比较有意思:

typedef struct
{
    const char *identifier;
    int address;
    const char *label;
} output_t;

const output_t outputs[] =
{
    { .identifier = "y402", .address = 0x2081, .label = "SV 1NP SCHODY" },
    { .identifier = "y401", .address = 0x2080, .label = "SV 2NP KUCHYN LINKA" },
    { .identifier = "y403", .address = 0x2082, .label = "SV 1NP OBYVAK KRB STR" },
    { .identifier = "y404", .address = 0x2083, .label = "SV 1NP OBYVAK KRB KRJ" },
    { .identifier = "y405", .address = 0x2084, .label = "SV 2NP LOZNICE KRJ" },
    { .identifier = "y406", .address = 0x2085, .label = "SV 2NP LOZNICE STR" },
    { .identifier = "y407", .address = 0x2086, .label = "SV 1NP WC" },
    { .identifier = "y409", .address = 0x2088, .label = "SV 0NP KOMORA KRJ" },
    { .identifier = "y410", .address = 0x2089, .label = "SV 0NP KOMORA STR" },
    { .identifier = "y411", .address = 0x208a, .label = "SV 0NP GARAZ 1" },
    { .identifier = "y412", .address = 0x208b, .label = "SV 0NP VRATA" },
    { .identifier = "y413", .address = 0x208c, .label = "SV 0NP GARAZ 2" },
    { .identifier = "y414", .address = 0x208d, .label = "SV 0NP GARAZ 3" },
    { .identifier = "y415", .address = 0x208e, .label = "SV 0NP SCHODY" },
    { .identifier = "y416", .address = 0x208f, .label = "SV 0NP CHODBA" },
    { .identifier = "y501", .address = 0x20a0, .label = "SV 1NP VCHOD" },
    { .identifier = "y503", .address = 0x20a2, .label = "SV 1NP PRISTAVBA" },
    { .identifier = "y505", .address = 0x20a4, .label = "SV 0NP SPIZIRNA STR" },
    { .identifier = "y506", .address = 0x20a5, .label = "SV 0NP SPIZIRNA KRJ" },
    { .identifier = "y509", .address = 0x20a8, .label = "SV 2NP LOZNICE BOD L" },
    { .identifier = "y510", .address = 0x20a9, .label = "SV 2NP LOZNICE BOD P" },
    { .identifier = "y511", .address = 0x20aa, .label = "SV 1NP KOUPELNA ZRCADLO" },
    { .identifier = "y512", .address = 0x20ab, .label = "ZA 1NP KOUPELNA ZRCADLO" },
    { .identifier = "y513", .address = 0x20ac, .label = "SV 2NP KOUPELNA" },
    { .identifier = "y514", .address = 0x20ad, .label = "SV 2NP KOUPELNA SPRCHA" },
    { .identifier = "y515", .address = 0x20ae, .label = "SV 2NP JIDELNA KRJ" },
    { .identifier = "y516", .address = 0x20af, .label = "SV 2NP JIDELNA STR" },
    { .identifier = "y603", .address = 0x20c2, .label = "SV 2NP CHODBA" },
    { .identifier = "y605", .address = 0x20c4, .label = "SV 2NP KUCHYN STRED" },
    { .identifier = "y606", .address = 0x20c5, .label = "SV 2NP KUCHYN BAR" },
    { .identifier = "y607", .address = 0x20c6, .label = "SV 2NP OBYVAK TV KRJ" },
    { .identifier = "y608", .address = 0x20c7, .label = "SV 2NP OBYVAK TV STR" },
    { .identifier = "y609", .address = 0x20c8, .label = "SV 1NP JIDELNA KRJ" },
    { .identifier = "y610", .address = 0x20c9, .label = "SV 1NP JIDELNA STR" },
    { .identifier = "y611", .address = 0x20ca, .label = "SV 1NP KOUPELNA BOD" },
    { .identifier = "y613", .address = 0x20cc, .label = "SV 1NP KUCHYN LINKA" },
    { .identifier = "y614", .address = 0x20cd, .label = "SV 1NP KUCHYN TROUBA" },
    { .identifier = "y615", .address = 0x20ce, .label = "SV 1NP KUCHYN ZARIVKA L" },
    { .identifier = "y616", .address = 0x20cf, .label = "SV 1NP KUCHYN ZARIVKA P" },
    { .identifier = "y701", .address = 0x20e0, .label = "SV 2NP OBYVAK STRED KRJ" },
    { .identifier = "y702", .address = 0x20e1, .label = "SV 2NP OBYVAK STRED STR" },
    { .identifier = "y703", .address = 0x20e2, .label = "SV 1NP OBYVAK OKNO STR" },
    { .identifier = "y704", .address = 0x20e3, .label = "SV 1NP OBYVAK OKNO KRJ" },
    { .identifier = "y705", .address = 0x20e4, .label = "SV 1NP CHODBA PRISTAVBA" },
    { .identifier = "y706", .address = 0x20e5, .label = "SV 1NP CHODBA KOUPELNA" },
    { .identifier = "y707", .address = 0x20e6, .label = "SV 2NP KOUPELNA ZRCADLO" },
    { .identifier = "y709", .address = 0x20e8, .label = "SV 2NP PRACOVNA KRJ" },
    { .identifier = "y710", .address = 0x20e9, .label = "SV 2NP PRACOVNA STR" },
    { .identifier = "y711", .address = 0x20ea, .label = "SV 2NP KUCHYN SOKL" },
    { .identifier = "y712", .address = 0x20eb, .label = "SV 2NP KUCHYN SIKMINA" },
    { .identifier = "y713", .address = 0x20ec, .label = "SV 1NP LOZNICE STR" },
    { .identifier = "y714", .address = 0x20ed, .label = "SV 1NP LOZNICE KRJ" },
    { .identifier = "y715", .address = 0x20ee, .label = "SV 1NP TECH MISTNOST" },
    { .identifier = "y801", .address = 0x2100, .label = "SV 3NP POKOJ 1 JIH" },
    { .identifier = "y802", .address = 0x2101, .label = "SV 3NP POKOJ 1 SEVER" },
    { .identifier = "y803", .address = 0x2102, .label = "SV 3NP CHODBA ZAPAD" },
    { .identifier = "y804", .address = 0x2103, .label = "SV 2NP SCHODY" },
    { .identifier = "y805", .address = 0x2104, .label = "SV 3NP CHODBA VYCHOD" },
    { .identifier = "y807", .address = 0x2106, .label = "SV 3NP POKOJ 2 JIH" },
    { .identifier = "y808", .address = 0x2107, .label = "SV 3NP POKOJ 2 SEVER" },

    { .identifier = "c101", .address = 0x4064, .label = "SV DUM SV" },
    { .identifier = "c102", .address = 0x4065, .label = "SV DUM JV" },
    { .identifier = "c103", .address = 0x4066, .label = "SV DUM JZ" },
    { .identifier = "c104", .address = 0x4067, .label = "SV DUM SZ" },
    { .identifier = "c105", .address = 0x4068, .label = "SV PARKING" },
    { .identifier = "c106", .address = 0x4069, .label = "SV ZAHR J" },
    { .identifier = "c107", .address = 0x406a, .label = "SV RODODEN" },
    { .identifier = "c108", .address = 0x406b, .label = "SV GARAZ" },
    { .identifier = "c109", .address = 0x406c, .label = "SV CHALOUP" },
    { .identifier = "c110", .address = 0x406d, .label = "SV STUDNA" },
    { .identifier = "c111", .address = 0x406e, .label = "SV ZAHR S" },

    { .identifier = "c200", .address = 0x40c7, .label = "MB LATCH" },
    { .identifier = "c201", .address = 0x40c8, .label = "MB SENDING" },
    { .identifier = "c202", .address = 0x40c9, .label = "MB SUCCESS" },
    { .identifier = "c203", .address = 0x40ca, .label = "MB ERROR" },

    { .identifier = NULL, .address = 0, .label = NULL }
};

mosquitto 相关的函数,可以参考这个文章:
mosquitto库中常见的函数应用总结
或者是:
mosquitto.h

现在列出我对项目作者的源码分析:

#include<errno.h>
#include<stdbool.h>
#include<stddef.h>
#include<stdint.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>

#include<modbus.h>
#include<mosquitto.h>

#define DEVICE "/dev/ttyS0"
#define MQTT_PREFIX "plc/"
#define MQTT_HOST "localhost"
#define MQTT_PORT 1883
#define MQTT_KEEPALIVE 60

typedef struct
{
    const char *identifier;
    int address;
    const char *label;
} output_t;

const output_t outputs[] =
{
    { .identifier = "y402", .address = 0x2081, .label = "SV 1NP SCHODY" },
    { .identifier = "y401", .address = 0x2080, .label = "SV 2NP KUCHYN LINKA" },
    { .identifier = "y403", .address = 0x2082, .label = "SV 1NP OBYVAK KRB STR" },
    { .identifier = "y404", .address = 0x2083, .label = "SV 1NP OBYVAK KRB KRJ" },
    { .identifier = "y405", .address = 0x2084, .label = "SV 2NP LOZNICE KRJ" },
    { .identifier = "y406", .address = 0x2085, .label = "SV 2NP LOZNICE STR" },
    { .identifier = "y407", .address = 0x2086, .label = "SV 1NP WC" },
    { .identifier = "y409", .address = 0x2088, .label = "SV 0NP KOMORA KRJ" },
    { .identifier = "y410", .address = 0x2089, .label = "SV 0NP KOMORA STR" },
    { .identifier = "y411", .address = 0x208a, .label = "SV 0NP GARAZ 1" },
    { .identifier = "y412", .address = 0x208b, .label = "SV 0NP VRATA" },
    { .identifier = "y413", .address = 0x208c, .label = "SV 0NP GARAZ 2" },
    { .identifier = "y414", .address = 0x208d, .label = "SV 0NP GARAZ 3" },
    { .identifier = "y415", .address = 0x208e, .label = "SV 0NP SCHODY" },
    { .identifier = "y416", .address = 0x208f, .label = "SV 0NP CHODBA" },
    { .identifier = "y501", .address = 0x20a0, .label = "SV 1NP VCHOD" },
    { .identifier = "y503", .address = 0x20a2, .label = "SV 1NP PRISTAVBA" },
    { .identifier = "y505", .address = 0x20a4, .label = "SV 0NP SPIZIRNA STR" },
    { .identifier = "y506", .address = 0x20a5, .label = "SV 0NP SPIZIRNA KRJ" },
    { .identifier = "y509", .address = 0x20a8, .label = "SV 2NP LOZNICE BOD L" },
    { .identifier = "y510", .address = 0x20a9, .label = "SV 2NP LOZNICE BOD P" },
    { .identifier = "y511", .address = 0x20aa, .label = "SV 1NP KOUPELNA ZRCADLO" },
    { .identifier = "y512", .address = 0x20ab, .label = "ZA 1NP KOUPELNA ZRCADLO" },
    { .identifier = "y513", .address = 0x20ac, .label = "SV 2NP KOUPELNA" },
    { .identifier = "y514", .address = 0x20ad, .label = "SV 2NP KOUPELNA SPRCHA" },
    { .identifier = "y515", .address = 0x20ae, .label = "SV 2NP JIDELNA KRJ" },
    { .identifier = "y516", .address = 0x20af, .label = "SV 2NP JIDELNA STR" },
    { .identifier = "y603", .address = 0x20c2, .label = "SV 2NP CHODBA" },
    { .identifier = "y605", .address = 0x20c4, .label = "SV 2NP KUCHYN STRED" },
    { .identifier = "y606", .address = 0x20c5, .label = "SV 2NP KUCHYN BAR" },
    { .identifier = "y607", .address = 0x20c6, .label = "SV 2NP OBYVAK TV KRJ" },
    { .identifier = "y608", .address = 0x20c7, .label = "SV 2NP OBYVAK TV STR" },
    { .identifier = "y609", .address = 0x20c8, .label = "SV 1NP JIDELNA KRJ" },
    { .identifier = "y610", .address = 0x20c9, .label = "SV 1NP JIDELNA STR" },
    { .identifier = "y611", .address = 0x20ca, .label = "SV 1NP KOUPELNA BOD" },
    { .identifier = "y613", .address = 0x20cc, .label = "SV 1NP KUCHYN LINKA" },
    { .identifier = "y614", .address = 0x20cd, .label = "SV 1NP KUCHYN TROUBA" },
    { .identifier = "y615", .address = 0x20ce, .label = "SV 1NP KUCHYN ZARIVKA L" },
    { .identifier = "y616", .address = 0x20cf, .label = "SV 1NP KUCHYN ZARIVKA P" },
    { .identifier = "y701", .address = 0x20e0, .label = "SV 2NP OBYVAK STRED KRJ" },
    { .identifier = "y702", .address = 0x20e1, .label = "SV 2NP OBYVAK STRED STR" },
    { .identifier = "y703", .address = 0x20e2, .label = "SV 1NP OBYVAK OKNO STR" },
    { .identifier = "y704", .address = 0x20e3, .label = "SV 1NP OBYVAK OKNO KRJ" },
    { .identifier = "y705", .address = 0x20e4, .label = "SV 1NP CHODBA PRISTAVBA" },
    { .identifier = "y706", .address = 0x20e5, .label = "SV 1NP CHODBA KOUPELNA" },
    { .identifier = "y707", .address = 0x20e6, .label = "SV 2NP KOUPELNA ZRCADLO" },
    { .identifier = "y709", .address = 0x20e8, .label = "SV 2NP PRACOVNA KRJ" },
    { .identifier = "y710", .address = 0x20e9, .label = "SV 2NP PRACOVNA STR" },
    { .identifier = "y711", .address = 0x20ea, .label = "SV 2NP KUCHYN SOKL" },
    { .identifier = "y712", .address = 0x20eb, .label = "SV 2NP KUCHYN SIKMINA" },
    { .identifier = "y713", .address = 0x20ec, .label = "SV 1NP LOZNICE STR" },
    { .identifier = "y714", .address = 0x20ed, .label = "SV 1NP LOZNICE KRJ" },
    { .identifier = "y715", .address = 0x20ee, .label = "SV 1NP TECH MISTNOST" },
    { .identifier = "y801", .address = 0x2100, .label = "SV 3NP POKOJ 1 JIH" },
    { .identifier = "y802", .address = 0x2101, .label = "SV 3NP POKOJ 1 SEVER" },
    { .identifier = "y803", .address = 0x2102, .label = "SV 3NP CHODBA ZAPAD" },
    { .identifier = "y804", .address = 0x2103, .label = "SV 2NP SCHODY" },
    { .identifier = "y805", .address = 0x2104, .label = "SV 3NP CHODBA VYCHOD" },
    { .identifier = "y807", .address = 0x2106, .label = "SV 3NP POKOJ 2 JIH" },
    { .identifier = "y808", .address = 0x2107, .label = "SV 3NP POKOJ 2 SEVER" },

    { .identifier = "c101", .address = 0x4064, .label = "SV DUM SV" },
    { .identifier = "c102", .address = 0x4065, .label = "SV DUM JV" },
    { .identifier = "c103", .address = 0x4066, .label = "SV DUM JZ" },
    { .identifier = "c104", .address = 0x4067, .label = "SV DUM SZ" },
    { .identifier = "c105", .address = 0x4068, .label = "SV PARKING" },
    { .identifier = "c106", .address = 0x4069, .label = "SV ZAHR J" },
    { .identifier = "c107", .address = 0x406a, .label = "SV RODODEN" },
    { .identifier = "c108", .address = 0x406b, .label = "SV GARAZ" },
    { .identifier = "c109", .address = 0x406c, .label = "SV CHALOUP" },
    { .identifier = "c110", .address = 0x406d, .label = "SV STUDNA" },
    { .identifier = "c111", .address = 0x406e, .label = "SV ZAHR S" },

    { .identifier = "c200", .address = 0x40c7, .label = "MB LATCH" },
    { .identifier = "c201", .address = 0x40c8, .label = "MB SENDING" },
    { .identifier = "c202", .address = 0x40c9, .label = "MB SUCCESS" },
    { .identifier = "c203", .address = 0x40ca, .label = "MB ERROR" },

    { .identifier = NULL, .address = 0, .label = NULL }
};

modbus_t *mb = NULL;

struct mosquitto *mosq = NULL;

void mb_connect(const char *device)
{
    mb=modbus_new_rtu(device, 38400, 'O', 8, 1);
    //modbus_new_rtu函数会生成并初始化一个modbus的结构体来在串行线路中使用RTU模式进行通讯
    /*
      函数原型:modbus_t *modbus_new_rtu(const char *device,int baud,char parity,int data_bit,int stop_bit);
      device 指定OS处理的串行端口的名称,比如 /dev/ttyS0 or /dev/ttyUSB0
      baud 参数指定连接的波特率,比如9600, 19200, 57600, 115200等.
      parity 参数代表奇偶检验位,有如下值:
          N:无奇偶校验
          E:偶数校验
          O:奇数校验
      data_bit 参数指定数据的位数,允许值有: 5, 6, 7 ,8.
      stop_bit 参数指定停止位位数,允许值有1和2.
      The modbus_new_rtu() function shall return a pointer to a modbus_t structure if successful.
    */  

    if(mb==NULL)
    {
        fprintf(stderr, "modbus_new_rtu: Call failed\n");
        exit(EXIT_FAILURE);
    }

    if(modbus_set_slave(mb,1)==-1)//set the slave number in the libmodbus context.即设定从机号码
    {
      /*
         函数原型:int modbus_set_slave(modbus_t *ctx, int slave);
         This function shall return 0 if successful.
         Otherwise it shall return -1 and set errno to one of the values defined below.
      */
        fprintf(stderr, "modbus_set_slave: %s\n", modbus_strerror(errno));
        exit(EXIT_FAILURE);
    }

    if(modbus_connect(mb)==-1) //establish a Modbus connection
    {
        /*function shall return 0 if successful. 
         Otherwise it shall return -1 and set errno to one of the values defined by the system calls of the underlying platform.
        */
        fprintf(stderr, "modbus_connect: %s\n", modbus_strerror(errno));
        exit(EXIT_FAILURE);
    }
}

void mb_write_bit(int address, bool state)
{
    if(modbus_write_bit(mb,address,state)==-1)
    {
        fprintf(stderr, "modbus_write_bit: %s\n", modbus_strerror(errno));
        exit(EXIT_FAILURE);
    }
    /*
      函数原型:
         int modbus_write_bit(modbus_t *ctx, int addr, int status);
      函数描述:
         The modbus_write_bit() function shall write the status of status at the address addr of the remote device. 
         The value must be set to TRUE or FALSE.
         The function uses the Modbus function code 0x05 (force single coil).
    */
    
}

void mb_read_bit(int address, bool *state)
{
    uint8_t buf;
    //在C语言中有这样的定义:typedef unsigned char uint8_t;

    if(modbus_read_bits(mb, address, 1, &buf) == -1)
    {
        fprintf(stderr, "modbus_read_bits: %s\n", modbus_strerror(errno));
        exit(EXIT_FAILURE);
    }
    /*
      函数原型:int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
      函数描述:This function shall read the status of the nb bits (coils) to the address addr of the remote device.
               The result of reading is stored in dest array as unsigned bytes (8 bits) set to TRUE or FALSE.
                 
    */

    *state = buf;
}

void mqtt_connect_callback(struct mosquitto *mosq, void *obj, int rc)
{  //这个回调函数的作用大概就是,一旦连接成功,就去订阅主题,并且主题有两种。
    if(rc==0)
    {
        for(size_t i = 0; outputs[i].identifier != NULL; i++)
        {
            char *topic = malloc(strlen(MQTT_PREFIX) + strlen(outputs[i].identifier) + 4 + 1);

            if(topic == NULL)
            {
                fprintf(stderr, "malloc: Not enough memory\n");
                exit(EXIT_FAILURE);
            }

            strcpy(topic, MQTT_PREFIX);
            strcat(topic, outputs[i].identifier);//strcat函数用于字符串连接
            strcat(topic, "/set");

            if(mosquitto_subscribe(mosq, NULL, topic, 1) != MOSQ_ERR_SUCCESS)
            {
                fprintf(stderr, "mosquitto_subscribe: Call failed\n");
            }
          /*
            功能:订阅主题函数
            函数原型:libmosq_EXPORT int mosquitto_subscribe(struct mosquitto *mosq,int *mid,const char *sub,int qos)
            参数:
                mosq:a valid mosquitto instance.
                mid:指向int的指针。如果不为NULL,则函数会将其设置为该特定消息的消息ID。
                     然后可以将其与订阅回调一起使用,以确定何时发送消息。
                sub:主题名称,订阅模式。
                qos:the requested Quality of Service for this subscription.
             返回值:
                MOSQ_ERR_SUCCESS: on success.
                MOSQ_ERR_INVAL: if the input parameters were invalid.
                MOSQ_ERR_NOMEM: if an out of memory condition occurred.
                MOSQ_ERR_NO_CONN: if the client isn’t connected to a broker.
                MOSQ_ERR_MALFORMED_UTF8: if the topic is not valid UTF-8
                MOSQ_ERR_OVERSIZE_PACKET: if the resulting packet would be larger than supported by the broker.
          */
            free(topic);
        }

        for(size_t i = 0; outputs[i].identifier != NULL; i++)
        {
            char *topic = malloc(strlen(MQTT_PREFIX) + strlen(outputs[i].identifier) + 7 + 1);

            if (topic == NULL)
            {
                fprintf(stderr, "malloc: Not enough memory\n");
                exit(EXIT_FAILURE);
            }

            strcpy(topic,MQTT_PREFIX);
            strcat(topic,outputs[i].identifier);
            strcat(topic,"/toggle");

            if(mosquitto_subscribe(mosq, NULL, topic, 1) != MOSQ_ERR_SUCCESS)
            {
                fprintf(stderr, "mosquitto_subscribe: Call failed\n");
            }

            free(topic);
        }
    }
    else
    {
        fprintf(stderr, "mqtt_connect_callback: Connection failed\n");
    }
}

/*
  struct mosquitto_message
  {
     int mid;//消息序号ID
     char *topic; //主题
     void *payload; //主题内容 ,MQTT 中有效载荷
     int payloadlen; //消息的长度,单位是字节
     int qos; //服务质量
     bool retain; //是否保留消息
  };
*/

void mqtt_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message)
{
    for(size_t i = 0; outputs[i].identifier!=NULL;i++)
    {
        char *topic = malloc(strlen(MQTT_PREFIX) + strlen(outputs[i].identifier) + 4 + 1);

        if(topic == NULL)
        {
            fprintf(stderr, "malloc: Not enough memory\n");
            exit(EXIT_FAILURE);
        }

        strcpy(topic, MQTT_PREFIX);
        strcat(topic, outputs[i].identifier);
        strcat(topic, "/set");

        if(strcmp(message->topic, topic) == 0)//比较message->topic和topic,如果两者相同,就进去执行
        {
            bool ok = false;

            if(message->payloadlen==4&&strcmp(message->payload, "true") == 0)
            {   //如果消息长度是4个字节并且消息的主题内容是“true"
                mb_write_bit(outputs[i].address, true);
                ok = true;
            }
            else if(message->payloadlen==5&&strcmp(message->payload, "false") == 0)
            {
                //如果消息长度是5个字节并且消息的主题内容是“false"
                mb_write_bit(outputs[i].address, false);
                ok = true;
            }

            if(ok)
            {
                char *response = malloc(strlen(topic) + 3 + 1);

                if (response == NULL)
                {
                    fprintf(stderr, "malloc: Not enough memory\n");
                    exit(EXIT_FAILURE);
                }

                strcpy(response, topic);
                strcat(response, "/ok");

                if(mosquitto_publish(mosq, NULL, response, 0, NULL, 1, false) != MOSQ_ERR_SUCCESS)
                {
                    fprintf(stderr, "mosquitto_publish: Call failed\n");
                }
                /*
                   函数原型:ibmosq_EXPORT int mosquitto_publish(struct mosquitto *mosq,int *mid,const char *topic,
                            int payloadlen,const void *payload,int qos,bool retain)
                   函数作用: Publish a message on a given topic.
                   函数参数:
                        mosq:有效的mosquitto实例,客户端
                        mid:指向int的指针。如果不为NULL,则函数会将其设置为该特定消息的消息ID。
                             然后可以将其与发布回调一起使用,以确定何时发送消息。
                             请注意,尽管MQTT协议不对QoS = 0的消息使用消息ID,但libmosquitto为其分配了消息ID,
                             以便可以使用此参数对其进行跟踪
                        topic:要发布的主题,以null结尾的字符串
                        payloadlen:有效负载的大小(字节),有效值在0到268,435,455之间;主题消息的内容长度
                        payload:主题消息的内容,指向要发送的数据的指针,如果payloadlen >0,则它必须是有效的存储位置
                        qos:整数值0、1、2指示要用于消息的服务质量。
                        retain:设置为true以保留消息。
                */
                free(response);
            }
        }
        free(topic);
    }

    for (size_t i = 0; outputs[i].identifier != NULL; i++)
    {
        char *topic = malloc(strlen(MQTT_PREFIX) + strlen(outputs[i].identifier) + 7 + 1);

        if(topic == NULL)
        {
            fprintf(stderr, "malloc: Not enough memory\n");
            exit(EXIT_FAILURE);
        }

        strcpy(topic,MQTT_PREFIX);
        strcat(topic, outputs[i].identifier);
        strcat(topic, "/toggle");

        if(strcmp(message->topic, topic) == 0)//比较message->topic和topic,如果两者相同,就进去执行
        {
            bool ok = false;
            if(message->payloadlen==0)//如果消息的长度为0
            {
                bool state;
                mb_read_bit(outputs[i].address,&state);
                mb_write_bit(outputs[i].address,!state);
                ok = true;
            }

            if(ok) //如果消息的长度不为0
            {
                char *response = malloc(strlen(topic) + 3 + 1);
                if(response == NULL)
                {
                    fprintf(stderr, "malloc: Not enough memory\n");
                    exit(EXIT_FAILURE);
                }

                strcpy(response,topic);
                strcat(response,"/ok");

                if(mosquitto_publish(mosq, NULL, response, 0, NULL, 1, false) != MOSQ_ERR_SUCCESS)
                {
                    fprintf(stderr, "mosquitto_publish: Call failed\n");
                }
                free(response);
            }
        }
        free(topic);
    }
}

void cleanup(void) //在程序退出时作善后处理
{
    if(mosq != NULL)
    {
        mosquitto_destroy(mosq);
    }

    mosquitto_lib_cleanup();

    if(mb != NULL)
    {
        modbus_close(mb);
        modbus_free(mb);
    }
}

int main(int argc, char **argv)
{
    if(atexit(cleanup)!=0)
    {
      /*需要在程序退出时做必要处理。方法就是用atexit()函数来注册程序正常终止时要被调用的函数,在这里就是cleanup函数*/
      /*处理函数的调用顺序与其注册的顺序相反,也即最先注册的最后调用,最后注册的最先调用。*/
      fprintf(stderr, "atexit: Call failed\n");
      exit(EXIT_FAILURE); //EXIT_FAILURE就是1
    }

    mb_connect(DEVICE); //DEVICE是端口

    mosquitto_lib_init();//Must be called before any other mosquitto functions.This function is not thread safe.

    mosq = mosquitto_new(NULL, true, NULL); //mosq是一个struct mosquitto类型的指针
    /*
      Create a new mosquitto client instance.
      函数原型: libmosq_EXPORT struct mosquitto *mosquitto_new(const char *id,bool clean_session,void *obj)
      id: String to use as the client id.If NULL, a random client id will be generated,and clean_session must be true.
      clean_session: set to true to instruct the broker to clean all messages and subscriptions on disconnect, false                       to instruct it to keep them.Note that a client will never discard its own outgoing messages on                        disconnect.Calling mosquitto_connect or mosquitto_reconnect will cause the messages to be resent.                     Use mosquitto_reinitialise to reset a client to its original state.Must be set to true if the                         id parameter is NULL.
                     设置为true以指示代理在断开连接时清除所有消息和订阅,设置为false以指示其保留它们,客户端将永远不会                     在断开连接时丢弃自己的传出消息。调用mosquitto_connect或mosquitto_reconnect将导致重新发送消息。使                     用mosquitto_reinitialise将客户端重置为其原始状态。如果id参数为NULL,则必须将其设置为true。
                     简言之:就是断开后是否保留订阅信息true/false
      obj: A user pointer that will be passed as an argument to any callbacks that are specified.
    */

    if(mosq == NULL)
    {
        fprintf(stderr, "mosquitto_new: Call failed\n");
        exit(EXIT_FAILURE);
    }
    
   /*
     服务端发送CONNACK报文响应从客户端收到的CONNECT报文。服务端发送给客户端的第一个报文必须是CONNACK
     如果客户端在合理的时间内没有收到服务端的CONNACK报文,客户端应该关闭网络连接.
   */
    
    mosquitto_connect_callback_set(mosq, mqtt_connect_callback);
    //Set the connect callback.This is called when the broker sends a CONNACK message in response to a connection.
    /*
       函数原型:
           libmosq_EXPORT void mosquitto_connect_callback_set(struct mosquitto *mosq,
                  void (*on_connect)(struct mosquitto *, void *, int));
       函数参数:
           mosq:a valid mosquitto instance.
           on_connect:a callback function in the following form: 
                      void callback(struct mosquitto *mosq, void *obj, int rc)
       回调参数:
           mosq:the mosquitto instance making the callback.
           obj:the user data provided in mosquitto_new
           rc:the return code of the connection response.具体情况要视MQTT协议版本而定。在本程序中,
           rc为0就意味着连接成功
    */
   
    mosquitto_message_callback_set(mosq, mqtt_message_callback);
    //功能:消息回调函数,收到订阅的消息后调用。
    /*
        函数原型:libmosq_EXPORT void mosquitto_message_callback_set(
   	struct 	mosquitto *mosq,void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *));
        函数参数:
            mosq:有效的mosquitto实例,客户端。
            on_message:回调函数
        回调函数格式:void callback(struct mosquitto * mosq,void * obj,const struct mosquitto_message * message)
        回调函数参数:
             mosq:进行回调的mosquitto实例
             obj:mosquitto_new中提供的用户数据
             message: 消息数据,回调完成后,库将释放此变量和关联的内存,客户应复制其所需要的任何数据。
    */

    if(mosquitto_connect(mosq, MQTT_HOST, MQTT_PORT, MQTT_KEEPALIVE) != MOSQ_ERR_SUCCESS)
    {
        fprintf(stderr, "mosquitto_connect: Unable to connect\n");
        exit(EXIT_FAILURE);
    }
    /*
       函数原型:libmosq_EXPORT int mosquitto_connect(struct mosquitto *mosq,const char *host,int port,int keepalive)
       用来连接到一个MQTT代理
       函数参数:
          mosq:有效的mosquitto实例,mosquitto_new()返回的mosq.
          host: the hostname or ip address of the broker to connect to.
          port: the network port to connect to.  Usually 1883.
          keepalive:保持连接的秒数.即如果在这段时间内没有其他消息交换,则代理应该将PING消息发送到客户端的秒数,这个
                    项目的作者在这里设成了60秒
    */

    mosquitto_loop_forever(mosq, -1, 1);
    /*
       函数原型:libmosq_EXPORT int mosquitto_loop_forever(struct mosquitto *mosq,int timeout,int max_packets)
       功能:
          此函数在无限阻塞循环中调用loop(),对程序中只运行MQTT客户端循环的情况很有用。
          如果服务器连接丢失,它将处理重新连接,如果在回调中调用mosqitto_disconnect函数它将返回。
       函数参数:
          mosq: 有效的mosquitto实例,客户端
          timeout:
              超时之前,在select()调用中等待网络活动的最大毫秒数,设置为0以立即返回,设置为负可使用默认值为1000ms。
          max_packets:该参数当前未使用,应设为为1,以备来兼容
       函数返回值:
           MOSQ_ERR_SUCCESS 成功。
           MOSQ_ERR_INVAL 如果输入参数无效。
           MOSQ_ERR_NOMEM 如果发生内存不足的情况。
           MOSQ_ERR_NO_CONN 如果客户端未连接到代理。
           MOSQ_ERR_CONN_LOST 如果与代理的连接丢失。
           MOSQ_ERR_PROTOCOL 与代理进行通信时是否存在协议错误。
           MOSQ_ERR_ERRNO 如果系统调用返回错误。变量errno包含错误代码
       在CSDN上看到,有人把这个函数的作用理解成:循环处理网络消息
    */

    exit(EXIT_SUCCESS);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值