kopia lustrzana https://github.com/weetmuts/wmbusmeters
				
				
				
			
		
			
				
	
	
		
			251 wiersze
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			251 wiersze
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
/*
 | 
						|
 Copyright (C) 2021 Fredrik Öhrström
 | 
						|
 | 
						|
 This program is free software: you can redistribute it and/or modify
 | 
						|
 it under the terms of the GNU General Public License as published by
 | 
						|
 the Free Software Foundation, either version 3 of the License, or
 | 
						|
 (at your option) any later version.
 | 
						|
 | 
						|
 This program is distributed in the hope that it will be useful,
 | 
						|
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 GNU General Public License for more details.
 | 
						|
 | 
						|
 You should have received a copy of the GNU General Public License
 | 
						|
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
*/
 | 
						|
 | 
						|
// Utility to relay serial port SND_NR telegrams using mosquitto pub.
 | 
						|
 | 
						|
#define DEBUG 0
 | 
						|
 | 
						|
#define HOMEDIR "/home/yourdir"
 | 
						|
 | 
						|
#define _GNU_SOURCE
 | 
						|
#include <unistd.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/wait.h>
 | 
						|
#include <termios.h>
 | 
						|
#include <memory.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <errno.h>
 | 
						|
 | 
						|
typedef unsigned char uchar;
 | 
						|
 | 
						|
typedef uchar bool;
 | 
						|
#define true 1
 | 
						|
#define false 0
 | 
						|
 | 
						|
int open_serial(const char *tty, int baud_rate)
 | 
						|
{
 | 
						|
    int rc = 0;
 | 
						|
    speed_t speed = 0;
 | 
						|
    struct termios tios;
 | 
						|
    //int DTR_flag = TIOCM_DTR;
 | 
						|
 | 
						|
    int fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
 | 
						|
    if (fd == -1) {
 | 
						|
        usleep(1000*1000);
 | 
						|
        fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
 | 
						|
        if (fd == -1) goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    switch (baud_rate)
 | 
						|
    {
 | 
						|
    case 300:   speed = B300;  break;
 | 
						|
    case 600:   speed = B600;  break;
 | 
						|
    case 1200:   speed = B1200;  break;
 | 
						|
    case 2400:   speed = B2400;  break;
 | 
						|
    case 4800:   speed = B4800;  break;
 | 
						|
    case 9600:   speed = B9600;  break;
 | 
						|
    case 19200:  speed = B19200; break;
 | 
						|
    case 38400:  speed = B38400; break;
 | 
						|
    case 57600:  speed = B57600; break;
 | 
						|
    case 115200: speed = B115200;break;
 | 
						|
    default:
 | 
						|
        goto err;
 | 
						|
    }
 | 
						|
 | 
						|
    memset(&tios, 0, sizeof(tios));
 | 
						|
 | 
						|
    rc = cfsetispeed(&tios, speed);
 | 
						|
    if (rc < 0) goto err;
 | 
						|
    rc = cfsetospeed(&tios, speed);
 | 
						|
    if (rc < 0) goto err;
 | 
						|
 | 
						|
 | 
						|
    // CREAD=Enable receive CLOCAL=Ignore any Carrier Detect signal.
 | 
						|
    tios.c_cflag |= (CREAD | CLOCAL);
 | 
						|
    tios.c_cflag &= ~CSIZE;
 | 
						|
    tios.c_cflag |= CS8;
 | 
						|
    tios.c_cflag &=~ CSTOPB;
 | 
						|
    // Disable parity bit.
 | 
						|
    tios.c_cflag &=~ PARENB;
 | 
						|
 | 
						|
    tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
 | 
						|
    tios.c_iflag &= ~INPCK;
 | 
						|
 | 
						|
    tios.c_iflag &= ~(IXON | IXOFF | IXANY);
 | 
						|
 | 
						|
    tios.c_oflag &=~ OPOST;
 | 
						|
    tios.c_cc[VMIN] = 0;
 | 
						|
    tios.c_cc[VTIME] = 0;
 | 
						|
 | 
						|
    rc = tcsetattr(fd, TCSANOW, &tios);
 | 
						|
    if (rc < 0) goto err;
 | 
						|
 | 
						|
 | 
						|
    // This code can toggle DTR... maybe necessary
 | 
						|
    // for the pl2303 usb2serial driver/device.
 | 
						|
    //rc = ioctl(fd, TIOCMBIC, &DTR_flag);
 | 
						|
    //if (rc != 0) goto err;
 | 
						|
 | 
						|
    return fd;
 | 
						|
 | 
						|
err:
 | 
						|
    if (fd > 0)
 | 
						|
    {
 | 
						|
        close(fd);
 | 
						|
        fd = -1;
 | 
						|
    }
 | 
						|
    return fd;
 | 
						|
}
 | 
						|
 | 
						|
void manufacturerFlag(uchar lo, uchar hi, uchar *mfct)
 | 
						|
{
 | 
						|
    int m_field = (hi*256)+lo;
 | 
						|
    mfct[0] = (m_field/1024)%32+64;
 | 
						|
    mfct[1] = (m_field/32)%32+64;
 | 
						|
    mfct[2] = (m_field)%32+64;
 | 
						|
}
 | 
						|
 | 
						|
bool find_telegram(uchar *buf, size_t *len)
 | 
						|
{
 | 
						|
    size_t l = *len;
 | 
						|
    if (*len < 10) return false;
 | 
						|
    size_t tl = buf[0];
 | 
						|
    if (buf[1] != 0x44) return false;
 | 
						|
    if (tl+1 > l) return false;
 | 
						|
    size_t hl = 2*(tl+1)+1;
 | 
						|
    uchar hex[hl];
 | 
						|
    for (size_t i=0; i<tl+1; ++i)
 | 
						|
    {
 | 
						|
        sprintf(hex+i*2, "%02x", buf[i]);
 | 
						|
    }
 | 
						|
    hex[hl-1] = 0;
 | 
						|
    *len = l-tl-1;
 | 
						|
 | 
						|
    uchar id[9];
 | 
						|
    sprintf(id,   "%02x", buf[7]);
 | 
						|
    sprintf(id+2, "%02x", buf[6]);
 | 
						|
    sprintf(id+4, "%02x", buf[5]);
 | 
						|
    sprintf(id+6, "%02x", buf[4]);
 | 
						|
    id[8] = 0;
 | 
						|
 | 
						|
    uchar mfct[4];
 | 
						|
    manufacturerFlag(buf[2], buf[3], mfct);
 | 
						|
    mfct[3] = 0;
 | 
						|
 | 
						|
    char t[21];
 | 
						|
    snprintf(t, sizeof(t), "wmbusmeters/%s", id);
 | 
						|
    char *program = "mosquitto_pub";
 | 
						|
    const char *argv[8];
 | 
						|
    argv[0] = program;
 | 
						|
    argv[1] = "-h";
 | 
						|
    argv[2] = "localhost";
 | 
						|
    argv[3] = "-t";
 | 
						|
    argv[4] = t;
 | 
						|
    argv[5] = "-m";
 | 
						|
    argv[6] = hex;
 | 
						|
    argv[7] = 0;
 | 
						|
 | 
						|
    printf("Relaying id:%s mft:%s t:%s \"%s\"\n", id, mfct, t, hex);
 | 
						|
 | 
						|
    const char *env[2];
 | 
						|
    env[0] = "HOME=" HOMEDIR;
 | 
						|
    env[1] = 0;
 | 
						|
 | 
						|
    pid_t pid = fork();
 | 
						|
    int status;
 | 
						|
    if (pid == 0) {
 | 
						|
        close(0); // Close stdin
 | 
						|
#if (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD__)
 | 
						|
        execve(program, (char*const*)&argv[0], (char*const*)&env[0]);
 | 
						|
#else
 | 
						|
        execvpe(program, (char*const*)&argv[0], (char*const*)&env[0]);
 | 
						|
#endif
 | 
						|
        perror("Execvp failed:");
 | 
						|
    } else {
 | 
						|
        if (pid == -1) {
 | 
						|
            fprintf(stderr, "(shell) could not fork!\n");
 | 
						|
        }
 | 
						|
        waitpid(pid, &status, 0);
 | 
						|
        if (WIFEXITED(status)) {
 | 
						|
            // Child exited properly.
 | 
						|
            int rc = WEXITSTATUS(status);
 | 
						|
            if (rc != 0) {
 | 
						|
                fprintf(stderr, "(shell) %s exited with non-zero return code: %d\n", program, rc);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
int main()
 | 
						|
{
 | 
						|
    uchar buf[512];
 | 
						|
    size_t len = 0;
 | 
						|
    int count_to_clear = 0;
 | 
						|
    int fd = open_serial("/dev/ttyUSB0", 19200);
 | 
						|
 | 
						|
    if (fd == -1)
 | 
						|
    {
 | 
						|
        fprintf(stderr, "Could not open serial port.\n");
 | 
						|
    }
 | 
						|
 | 
						|
    for (;;)
 | 
						|
    {
 | 
						|
        if (len >= sizeof(buf))
 | 
						|
        {
 | 
						|
            // Oups, a lot of random data fills up the buffer with no telegrams!
 | 
						|
            len = 0;
 | 
						|
            count_to_clear = 0;
 | 
						|
            if (DEBUG) fprintf(stderr, "overflow clear\n");
 | 
						|
        }
 | 
						|
        if (DEBUG) fprintf(stderr, "Reading...");
 | 
						|
        ssize_t n = read(fd, buf+len, sizeof(buf)-len);
 | 
						|
        if (n <= 0)
 | 
						|
        {
 | 
						|
            if (errno == EBADF)
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Could not read from serial port.\n");
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            sleep(1);
 | 
						|
            count_to_clear++;
 | 
						|
            if (count_to_clear >= 2)
 | 
						|
            {
 | 
						|
                // After 2 seconds of no traffic, we clear the buffer.
 | 
						|
                // Any existing telegrams should have been found already.
 | 
						|
                len = 0;
 | 
						|
                count_to_clear = 0;
 | 
						|
                if (DEBUG) fprintf(stderr, "timeout clear\n");
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                if (DEBUG) fprintf(stderr, "no data.\n");
 | 
						|
            }
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        len+=n;
 | 
						|
        if (DEBUG) fprintf(stderr, "received data, buffer len %zu\n", len);
 | 
						|
        bool ok = find_telegram(buf, &len);
 | 
						|
        if (!ok)
 | 
						|
        {
 | 
						|
            if (DEBUG) fprintf(stderr, "No telegram found in data.");
 | 
						|
        }
 | 
						|
    }
 | 
						|
    close(fd);
 | 
						|
}
 |