teleinfo linky mode standard

Forum pour tous les autres objets : sondes météo, capteurs, actionneurs ...
Vous avez un besoin mais vous ne savez pas quel matériel choisir ? C'est ici.
Carthman
Messages : 96
Inscription : 22 sept. 2016, 16:27

Re: teleinfo linky mode standard

Message par Carthman »

Bonsoir,

J'ai essayé de regarder le code de plus près, je ne comprends pas grand chose !
J'ai compris que ce n'était pas un script à mettre dans la Crontab, mais bien un script géré directement par Domoticz, en Lua je suppose, mais je ne sais pas du tout comment l'adapter à mes besoins !

Comme précisé, j'aurai besoin de 4 informations importantes, que tu utilises peut-être déjà car il me semble que tu es également producteur, EAST, EAIT, SINSTS et SINSTI, que j'utiliserai dans 4 dispositifs différents.

Dans ton code, j'ai l'impression qu'un seul dispositif est utilisé ?

Bref, un peu perdu, j'aurais besoin d'un peu d'aide !!!
Carthman
Messages : 96
Inscription : 22 sept. 2016, 16:27

Re: teleinfo linky mode standard

Message par Carthman »

Bonsoir,

Je me permets de proposer "une alternative" au code que tu m'as donné.
Effectivement, c'est compliqué pour moi pour le moment de comprendre le python, alors j'ai créé un script bash que j'ai adapté à partir d'un autre trouvé sur le forum.

Ca pourrait peut-être servir à d'autres !

Code : Tout sélectionner

#!/bin/bash
#TELEINFO LINKY STANDARD
#Script pour lire les données Teleinfo d'un Linky en Mode Standard
#Il peut être adapté pour le Mode Historique en modifiant les données souhaitées
#En fonction du besoin, créer un Compteur Conso EDF, Injection EDF, Autoconso, un pourcentage de charge et un compteur intelligent P1
#Les données de production sont récupérées grâce au dispositif créé avec ce plugin https://github.com/rklomp/Domoticz-SMA-SunnyBoy
#A mettre dans la Crontab avec @reboot pour qu'il se lance au démarrage

#Variables utilisateur à modifier
DOMO_IP="192.168.0.1"           # IP Domoticz à renseigner
DOMO_PORT="1234"                # Port Domoticz à renseigner
TELEINFO_USB="12"               # Port USB où est branché le module Teleinfo
PUISSANCE="9000"                # Puissance du compteur en VA
CONSO_IDX="87"                  # IDX Compteur Conso EDF à renseigner
INJECTION_IDX="88"              # IDX Compteur Injection EDF à renseigner
P1_IDX="92"                     # IDX Compteur Intelligent P1 à renseigner
AUTOCONSO_IDX="90"              # IDX Compteur Autoconso à renseigner
CHARGE_IDX="91"                 # IDX Charge à renseigner
SMA_IDX="78"                    # IDX Plugin SMA
VALEUR_DECOMPTE="3"             # MAJ des compteurs toutes les 4 secondes environ (un passage du script prend en moyenne 1.3 Secondes)



DECOMPTE="$VALEUR_DECOMPTE"
sudo stty -F /dev/ttyUSB$TELEINFO_USB 9600 evenp -crtscts

while true
do


#Récupération de toutes les données dans la trame USB et traitement pour utilisation
TELEINFO=$(grep -E 'EAST|EAIT|SINSTS|SINSTI' /dev/ttyUSB$TELEINFO_USB -m 4 | sort | awk '{ print($1 $2) }' | sed 's/[a-z A-Z]//g')

#Récupération des données de production
PRODPV=$(curl -s "http://$DOMO_IP:$DOMO_PORT/json.htm?type=devices&rid=$SMA_IDX" | jq -r .result[]."Usage" | sed 's/[a-z A-Z]//g')
PRODPVKWH=$(curl -s "http://$DOMO_IP:$DOMO_PORT/json.htm?type=devices&rid=$SMA_IDX" | jq -r .result[]."Data" | sed 's/[a-z A-Z .]//g')



#Lecture des données récupérées
#EAIT = Energie Active Injectée Totale
#EAST = Energie Active Soutirée Totale
#SINSTI = Puissance Apparente Instantanée Injectée
#SINSTS = Puissance Apparente Instantanée Soutirée
#CHARGE = Pourcentage de Charge par rapport à l'abonnement - Peut-être utilisée pour faire du délestage
#AUTOCONSO = Autoconsommation instantanée en W
#AUTOCONSOKWH = Autoconsommation totale en Wh
#TOTALELEC = Consommation Totale en W (Autoconso + EDF)
EAIT=$(echo $TELEINFO | awk '{ print $1}')
EAST=$(echo $TELEINFO | awk '{ print $2}')
SINSTI=$(echo $TELEINFO | awk '{ print $3}')
SINSTS=$(echo $TELEINFO | awk '{ print $4}')
CHARGE=$(echo "scale=2; $SINSTS/$PUISSANCE*100" | bc -l)
AUTOCONSO=$(echo $(($PRODPV-$SINSTI)))
AUTOCONSOKWH=$(echo "$PRODPVKWH-$EAIT" | bc)
TOTALELEC=$(echo "$AUTOCONSO+$SINSTS" | bc)
ERREUR_TELEINFO="0"



#Tests pour affichage des données dans la console
#echo $TELEINFO
#echo "EAIT : $EAIT"
#echo "EAST : $EAST"
#echo "SINSTI : $SINSTI"
#echo "SINSTS : $SINSTS"
#echo "CHARGE : $CHARGE"
#echo "PRODPV : $PRODPV"
#echo "PRODPVKWH : $PRODPVKWH"
#echo "AUTOCONSO : $AUTOCONSO"
#echo "AUTOCONSOKWH : $AUTOCONSOKWH"
#echo "TOTALELEC : $TOTALELEC"
#echo "ERREUR : $ERREUR_TELEINFO"
#echo "DECOMPTE : $DECOMPTE"





if [[ $DECOMPTE = 0 ]] ; then

        #Données envoyées vers le compteur Conso EDF
        if [[ $SINSTS = +([0-9]) ]] && [[ $EAST = +([0-9]) ]] ; then
        curl -s "http://$DOMO_IP:$DOMO_PORT/json.htm?type=command&param=udevice&idx=$CONSO_IDX&nvalue=0&svalue=$SINSTS;$EAST" > /dev/null
        else ERREUR_TELEINFO="1"
        fi

        #Données envoyées vers le compteur Injection EDF
        if [[ $SINSTI = +([0-9]) ]] && [[ $EAIT = +([0-9]) ]] ; then
        curl -s "http://$DOMO_IP:$DOMO_PORT/json.htm?type=command&param=udevice&idx=$INJECTION_IDX&nvalue=0&svalue=$SINSTI;$EAIT" > /dev/null
        else ERREUR_TELEINFO="1"
        fi

        #Données envoyées vers le compteur Autoconso
        if [[ $AUTOCONSO = +([0-9]) ]] && [[ $AUTOCONSOKWH = +([0-9]) ]] ; then
        curl -s "http://$DOMO_IP:$DOMO_PORT/json.htm?type=command&param=udevice&idx=$AUTOCONSO_IDX&nvalue=0&svalue=$AUTOCONSO;$AUTOCONSOKWH" > /dev/null
        else ERREUR_TELEINFO="1"
        fi

        #Données envoyées vers le compteur intelligent P1
        if [[ $TOTALELEC = +([0-9]) ]] && [[ $ERREUR_TELEINFO = 0 ]] ; then
        curl -s "http://$DOMO_IP:$DOMO_PORT/json.htm?type=command&param=udevice&idx=$P1_IDX&nvalue=0&svalue=$EAST;$AUTOCONSOKWH;$EAIT;$AUTOCONSOKWH;$TOTALELEC;$PRODPV" > /dev/null
        fi

        DECOMPTE="$VALEUR_DECOMPTE+1"

fi


#Données envoyées vers le compteur de charge
curl -s "http://$DOMO_IP:$DOMO_PORT/json.htm?type=command&param=udevice&idx=$CHARGE_IDX&nvalue=0&svalue=$CHARGE" > /dev/null

DECOMPTE=$((DECOMPTE-1))

done
snania
Messages : 3
Inscription : 18 oct. 2018, 22:27

Re: teleinfo linky mode standard

Message par snania »

hello Carthman
TOn code m'interesse pas mal et j'ai commencé a l'adapter a ma situation, merci pour ton partage.
Par contre, pourrais tu me dire ce que tu récupère via ton "SMA_IDX"?
J'ai pensé vu le code a la production solaire, ce qui serait cool j'ai mis l'IDX de mon compteur kwh solaire mais j'ai une erreur.
Dans le calcul je vois qu'il dit que la valeur est trop basse et en regardant de plus prêt ça calcul (($PRODPV-$SINSTI)), je ne comprend pas trop ce que c'est derrière de ton coté?
knasson
Messages : 245
Inscription : 26 mars 2021, 08:59

Re: teleinfo linky mode standard

Message par knasson »

js-martin a écrit : 14 juil. 2018, 19:25 Bon, finalement j'ai pris ton code qui m'a fait gagné beaucoup de temps. Merci ;)

Chez moi, il plantait régulièrement (il faut dire que j'ai pas mal de lignes en erreur, je pense que mon dongle usb a du mal à suivre).

Donc, j'ai pris ton code, supprimé les trames EAST et SINSTS (qui seront toujours à zéro pour moi) et je le rends à usage unique : il prend 10 secondes pour récupérer 2-3 trames EAIT (total production) + SINSTI (production instantanée) et il s'arrête juste après avoir mis à jour domoticz.

Dans la contable, je lui fais faire une mise à jour toutes les 2 minutes.

Enedis devrait me repasser en mode historique (je ne sais pas si c'est une bonne idée).

ton code saccagé :

Code : Tout sélectionner

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <limits.h>
#include <sys/time.h>
#include <curl/curl.h>


//--------------------------------------------------
// DONNEES A MODIFIER EN FONCTION DE LA CONFIGURATION SOUHAITEE
//--------------------------------------------------
// Port Serie et Baud Rate
// Délimiteur (normalement 0x09 à 9600 bauds et 0x20 à 1200 bauds)
// Requête JSON pour Domoticz (CHANGER LE IDX AVEC LE NUMERO DE DEVICE)
//--------------------------------------------------
const char* SERIAL_PORT = "/dev/ttyUSB0";
const speed_t BAUD_RATE = B9600;
const char DEMILITER = 0x09;
const char* JSON_HEADER = "http://192.168.1.40:8080/json.htm?type=command&param=udevice&idx=735&nvalue=0&svalue=";
//--------------------------------------------------



/* Number of consecutive characters to read (max = 255) */
const unsigned int BUFFER_SIZE = 255;
/* Max number of characters in one line (<= BUFFER SIZE) */
const unsigned int MAX_LINE_SIZE = 150;
/* Min number of characters in one line (<= MAX_LINE_SIZE) */
const unsigned int MIN_LINE_SIZE = 5;

typedef struct {
	unsigned int EAST;
	unsigned int EAIT;
	unsigned int SINSTS;
	unsigned int SINSTI;
} Teleinfo;

typedef struct {
	int value;
	unsigned int elapsed_seconds;
} DomoticzDevice;


Teleinfo teleinfo = { .EAST = 0, .EAIT = 0, .SINSTS = 0, .SINSTI = 0 };
DomoticzDevice EDF = { .value = 0, .elapsed_seconds = 0 };

unsigned int send_message = 0; 
struct timeval tv1; struct timezone tz1;
struct timeval tv2; struct timezone tz2;

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0)
    {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS7;         /* 7-bit characters */
    tty.c_cflag |= PARENB;     /* parity enabled & even */
    tty.c_cflag &= ~CSTOPB;     /* 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    /* read characters, with max delay of half second to receive a char */
    tty.c_cc[VMIN] = BUFFER_SIZE;
    tty.c_cc[VTIME] = 5;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) 
    {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

int isCheckSumOk(char* line)
{
	unsigned int checksum_expected = 0;
	unsigned int checksum_real = 0;
	unsigned int i;

	checksum_expected = line[strlen(line) - 1];
	for (i = 0; i <= strlen(line) - 2; i++)
	{
		checksum_real += line[i];
	}
	checksum_real = (checksum_real & 0x3F) + 0x20;

	if (checksum_expected != checksum_real)
	{
		//printf("ERROR --> Checksum failed on line: \"%s\"\n", line);
		unsigned char *p;
		unsigned int linelen = strlen(line);
		for (p = line; linelen-- > 0; p++)
			//printf(" 0x%x", *p);
		//printf("\n");
		return -1;
	}

	return 0;
}

int strToUL(char* str)
{
	unsigned int val = 0;
	char *endptr;
	errno = 0;
	val = strtoul(str, &endptr, 10);
	if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0)) 
	{
		printf("ERROR --> Cannot convert to ul the string: \"%s\"\n", str);
		return -1;
	}
	else if (endptr == str)
	{
		printf("ERROR --> Cannot convert to ul an empty string: \"%s\"\n", str);
		return -1;
	}
	return (int)val;
}

int sendUrl(char* url)
{
	int ret = -1;
    CURL *curl;
    CURLcode res;
    curl = curl_easy_init();
    if (curl)
    {
        curl_easy_setopt(curl, CURLOPT_URL, url);
		curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
		curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); //Prevent "longjmp causes uninitialized stack frame" bug
		curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "deflate");
		curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60);
        //curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
        //curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
        res = curl_easy_perform(curl);
        if(res != CURLE_OK)
        {
			printf("ERROR --> Cannot send curl url: \"%s\"\n", url);
        }
        else
        {
			ret = 0;
		}

        curl_easy_cleanup(curl);
    }
    else
    {
		printf("ERROR --> Cannot init curl\n");
	}
	return ret;
}

int processLine(char* line)
{
	char label [MAX_LINE_SIZE];
	char elem1 [MAX_LINE_SIZE];
	char elem2 [MAX_LINE_SIZE];
	int val;
	unsigned int start = 0;
	unsigned int i = 0;
	// Find next elem
	label[0] = '\0';
	for (i = start; i <= strlen(line) - 2; i++)
	{
		// Find the delimiter
		if (line[i] == DEMILITER)
		{
			memcpy(label, &line[start], i-start);
			label[i-start] = '\0';
			break;
		}
	}
	// Find next elem
	start = i+1;
	elem1[0] = '\0';
	for (i = start; i <= strlen(line) - 2; i++)
	{
		// Find the delimiter
		if (line[i] == DEMILITER)
		{
			memcpy(elem1, &line[start], i-start);
			elem1[i-start] = '\0';
			break;
		}
	}
	// Find next elem
	start = i+1;
	elem2[0] = '\0';
	for (i = start; i <= strlen(line) - 2; i++)
	{
		// Find the delimiter
		if (line[i] == DEMILITER)
		{
			memcpy(elem2, &line[start], i-start);
			elem2[i-start] = '\0';
			break;
		}
	}

	//printf("DEBUG --> New line: %s - %s - %s\n", label, elem1, elem2);
	// Save value
//	if(strcmp(label, "EAST") == 0) {
//		val = strToUL(elem1);
//		if (val != -1) teleinfo.EAST = (unsigned int)val;
//		else return -1;
//	} else 
        if(strcmp(label, "EAIT") == 0) {
		val = strToUL(elem1);
		if (val != -1) teleinfo.EAIT = (unsigned int)val;
		else return -1;
	}
//	else if(strcmp(label, "SINSTS") == 0) {
//		val = strToUL(elem1);
//		if (val != -1) teleinfo.SINSTS = (unsigned int)val;
//		else return -1;
//	}
	else if(strcmp(label, "SINSTI") == 0) {
		val = strToUL(elem1);
		if (val != -1) teleinfo.SINSTI = (unsigned int)val;
		else return -1;
	}
}

int main()
{
    //freopen("out.txt", "a", stdout);
    int fd;
    int wlen;
    unsigned char line[MAX_LINE_SIZE];
    unsigned int linelen = 0;
    unsigned int pos = 0;
    fd = open(SERIAL_PORT, O_RDONLY | O_NOCTTY | O_SYNC);
    if (fd < 0)
    {
        printf("Error opening %s: %s\n", SERIAL_PORT, strerror(errno));
        return -1;
    }

	/* Get current time */
	gettimeofday(&tv1, &tz1);
	gettimeofday(&tv2, &tz2);

    /* Open serial port */
    set_interface_attribs(fd, BAUD_RATE);

    /* Receive characters */
    do
    {
        unsigned char buf[BUFFER_SIZE];
        int rdlen;
        int first_line = 1;

        rdlen = read(fd, buf, sizeof(buf) - 1);
        if (rdlen > 0)
        {
			int i = 0;
			while (i < rdlen)
			{
				// Discard all special characters
				const char c = buf[i];
				if (((c >= 0x00) && (c <= 0x01)) || ((c >= 0x04) && (c <= 0x08)))
				{
					//printf("DEBUG --> Char 0x%x discarded in line: \"%s\"\n", c, line);
					i++;
					continue;
				}

				// Get character
				line[pos] = c;

				// If line ending is found
				if (((c >= 0x02) && (c <= 0x03)) || ((c >= 0x0a) && (c <= 0x0f)))
				{
					// Close string
					line[pos] = 0;
					linelen = pos;
					pos = 0;

					// Discard first line as it may be truncated
					if (first_line)
					{
						first_line = 0;
					}
					// Discard lines too small
					else if (linelen < MIN_LINE_SIZE)
					{
						// Nothing to do
                        //printf("WARNING --> Line discarded because too small: \"%s\"\n", line);
					}
					// Discard incoherent lines
					else if (line[linelen-2] != DEMILITER)
					{
						//printf("ERROR --> Ending delimiter not found on line: \"%s\"\n", line);
					}
					else
					{
#ifdef DISPLAY_LINE_STRING
						//printf("Read %d: \"%s\"\n", linelen, line);
#endif
#ifdef DISPLAY_LINE_HEX
						unsigned char *p;
						//printf("Read %d:", linelen);
						for (p = line; linelen-- > 0; p++)
							//printf(" 0x%x", *p);
						//printf("\n");
#endif
						// Verify checksum
						if (isCheckSumOk(line) == 0)
						{
							//printf("SUCCESS --> Checksum OK on line: \"%s\"\n", line);
							processLine(line);
						}
					}
				}

				else if (pos == sizeof(line) - 1)
				{
					// Close string
					line[pos] = 0;
					linelen = pos;
					pos = 0;

					printf("ERROR: no new line read in line: \"%s\"\n", line);
				}

				else if (pos == sizeof(buf) - 1)
				{
					// Close string
					line[pos] = 0;
					linelen = pos;
					pos = 0;

					printf("ERROR: no new line read in buffer: \"%s\"\n", buf);
				}

				else
				{
					pos++;
				}

				i++;
			}

#ifdef DISPLAY_BUFFER_STRING
            buf[rdlen] = 0;
            printf("Read %d: \"%s\"\n", rdlen, buf);
#endif
#ifdef DISPLAY_BUFFER_HEX
            unsigned char *p;
            printf("Read %d:", rdlen);
            for (p = buf; rdlen-- > 0; p++)
                printf(" 0x%x", *p);
            printf("\n");
#endif
        }
        else if (rdlen < 0)
        {
            printf("Error from read: %d: %s\n", rdlen, strerror(errno));
        }


		/* Check if it's time to send a message to Domoticz */
		if (send_message)
		{
			send_message = 0;

			//Calculate values to send
			//int SINST = (int)(teleinfo.SINSTS) - (int)(teleinfo.SINSTI);
                        int SINST =  (int)(teleinfo.SINSTI);
			char SINST_str[12]; sprintf(SINST_str, "%d", SINST);

			//int EAT = (int)(teleinfo.EAST) - (int)(teleinfo.EAIT);
                        int EAT =  (int)(teleinfo.EAIT);
			char EAT_str[12]; sprintf(EAT_str, "%d", EAT);

			//Construction of message to send
			char message [1000];
			strcpy(message, JSON_HEADER);
			strcat(message, SINST_str);
			strcat(message, ";");
			strcat(message, EAT_str);
			//Send message
			if(sendUrl(message) == 0)
			{
				printf("DEBUG --> New message sent: \"%s\"\n", message);
                                exit(0);
			}
		}
		else
		{
			gettimeofday(&tv2, &tz2);
			if((tv2.tv_sec - tv1.tv_sec) >= 10)
			{
				send_message = 1;
				tv1.tv_sec = tv2.tv_sec;
			}
		}

        /* sleep and repeat read */
        //sleep(1);
    } while (1);
}


Installation Curl sur Raspberry :

Code : Tout sélectionner

sudo apt-get install libcurl4-openssl-dev
Compilation :

Code : Tout sélectionner

sudo cc driver_teleinfo.c -lcurl
Quand j'exécute le code ci-dessus après compilation,
voila ce que j'obtiens:

root@raspberrypi:/home/kan/TeleInfo# ./a.out
{
"status" : "ERR"
}
DEBUG --> New message sent: "http://192.168.1.38:8080/json.htm?type= ... 94;3905491"
root@raspberrypi:/home/kan/TeleInfo#

apparemment je récupère bien les valeurs mais avec une erreur et elles n'alimentent pas le compteur virtuel que j'ai créé sur le dashboard de domoticz
ou peut etre le probleme?
Merci de votre aide
cordialement
Jean-Claude

je m'interroge?
js-martin
Messages : 487
Inscription : 22 mars 2015, 22:08
Contact :

Re: teleinfo linky mode standard

Message par js-martin »

Un résultat normal du script est :
pi@raspberrypi ~/domoticz/scripts $ ./maj_linky_prod
{
"status" : "OK",
"title" : "Update Device"
}
{
"status" : "OK",
"title" : "Update Device"
}
L'erreur semble venir de l'adaptation du code.

Je te mets ma derniere version du code :

Code : Tout sélectionner

Apres MAJ 4,9 le totalisateur du compteur de production ne s'affiche pas. 
Solution = mettre un compteur virtuel P1
6 parametres, deux utiles (tot;0;0;0;instant;0)

504999;0;0;0;999;0

Code souce C :

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <limits.h>
#include <sys/time.h>
#include <curl/curl.h>


//--------------------------------------------------
// DONNEES A MODIFIER EN FONCTION DE LA CONFIGURATION SOUHAITEE
//--------------------------------------------------
// Port Serie et Baud Rate
// Délimiteur (normalement 0x09 à 9600 bauds et 0x20 à 1200 bauds)
// Requête JSON pour Domoticz (CHANGER LE IDX AVEC LE NUMERO DE DEVICE)
//--------------------------------------------------
const char* SERIAL_PORT = "/dev/ttyUSB0";
const speed_t BAUD_RATE = B9600;
const char DEMILITER = 0x09;
const char* JSON_HEADER = "http://ip_pi:8080/json.htm?type=command&param=udevice&idx=735&nvalue=0&svalue=";
//--------------------------------------------------



/* Number of consecutive characters to read (max = 255) */
const unsigned int BUFFER_SIZE = 255;
/* Max number of characters in one line (<= BUFFER SIZE) */
const unsigned int MAX_LINE_SIZE = 150;
/* Min number of characters in one line (<= MAX_LINE_SIZE) */
const unsigned int MIN_LINE_SIZE = 5;

typedef struct {
   unsigned int EAST;
   unsigned int EAIT;
   unsigned int SINSTS;
   unsigned int SINSTI;
} Teleinfo;

typedef struct {
   int value;
   unsigned int elapsed_seconds;
} DomoticzDevice;


Teleinfo teleinfo = { .EAST = 0, .EAIT = 0, .SINSTS = 0, .SINSTI = 0 };
DomoticzDevice EDF = { .value = 0, .elapsed_seconds = 0 };

unsigned int send_message = 0; 
struct timeval tv1; struct timezone tz1;
struct timeval tv2; struct timezone tz2;

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0)
    {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS7;         /* 7-bit characters */
    tty.c_cflag |= PARENB;     /* parity enabled & even */
    tty.c_cflag &= ~CSTOPB;     /* 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    /* read characters, with max delay of half second to receive a char */
    tty.c_cc[VMIN] = BUFFER_SIZE;
    tty.c_cc[VTIME] = 5;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) 
    {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

int isCheckSumOk(char* line)
{
   unsigned int checksum_expected = 0;
   unsigned int checksum_real = 0;
   unsigned int i;

   checksum_expected = line[strlen(line) - 1];
   for (i = 0; i <= strlen(line) - 2; i++)
   {
      checksum_real += line[i];
   }
   checksum_real = (checksum_real & 0x3F) + 0x20;

   if (checksum_expected != checksum_real)
   {
      //printf("ERROR --> Checksum failed on line: \"%s\"\n", line);
      unsigned char *p;
      unsigned int linelen = strlen(line);
      for (p = line; linelen-- > 0; p++)
         //printf(" 0x%x", *p);
      //printf("\n");
      return -1;
   }

   return 0;
}

int strToUL(char* str)
{
   unsigned int val = 0;
   char *endptr;
   errno = 0;
   val = strtoul(str, &endptr, 10);
   if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0)) 
   {
      printf("ERROR --> Cannot convert to ul the string: \"%s\"\n", str);
      return -1;
   }
   else if (endptr == str)
   {
      printf("ERROR --> Cannot convert to ul an empty string: \"%s\"\n", str);
      return -1;
   }
   return (int)val;
}

int sendUrl(char* url)
{
   int ret = -1;
    CURL *curl;
    CURLcode res;
    curl = curl_easy_init();
    if (curl)
    {
        curl_easy_setopt(curl, CURLOPT_URL, url);
      curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
      curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); //Prevent "longjmp causes uninitialized stack frame" bug
      curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "deflate");
      curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60);
        //curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
        //curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
        res = curl_easy_perform(curl);
        if(res != CURLE_OK)
        {
         printf("ERROR --> Cannot send curl url: \"%s\"\n", url);
        }
        else
        {
         ret = 0;
      }

        curl_easy_cleanup(curl);
    }
    else
    {
      printf("ERROR --> Cannot init curl\n");
   }
   return ret;
}

int processLine(char* line)
{
   char label [MAX_LINE_SIZE];
   char elem1 [MAX_LINE_SIZE];
   char elem2 [MAX_LINE_SIZE];
   int val;
   unsigned int start = 0;
   unsigned int i = 0;
   // Find next elem
   label[0] = '\0';
   for (i = start; i <= strlen(line) - 2; i++)
   {
      // Find the delimiter
      if (line[i] == DEMILITER)
      {
         memcpy(label, &line[start], i-start);
         label[i-start] = '\0';
         break;
      }
   }
   // Find next elem
   start = i+1;
   elem1[0] = '\0';
   for (i = start; i <= strlen(line) - 2; i++)
   {
      // Find the delimiter
      if (line[i] == DEMILITER)
      {
         memcpy(elem1, &line[start], i-start);
         elem1[i-start] = '\0';
         break;
      }
   }
   // Find next elem
   start = i+1;
   elem2[0] = '\0';
   for (i = start; i <= strlen(line) - 2; i++)
   {
      // Find the delimiter
      if (line[i] == DEMILITER)
      {
         memcpy(elem2, &line[start], i-start);
         elem2[i-start] = '\0';
         break;
      }
   }

   //printf("DEBUG --> New line: %s - %s - %s\n", label, elem1, elem2);
   // Save value
//   if(strcmp(label, "EAST") == 0) {
//      val = strToUL(elem1);
//      if (val != -1) teleinfo.EAST = (unsigned int)val;
//      else return -1;
//   } else 
        if(strcmp(label, "EAIT") == 0) {
      val = strToUL(elem1);
      if (val != -1) teleinfo.EAIT = (unsigned int)val;
      else return -1;
   }
//   else if(strcmp(label, "SINSTS") == 0) {
//      val = strToUL(elem1);
//      if (val != -1) teleinfo.SINSTS = (unsigned int)val;
//      else return -1;
//   }
   else if(strcmp(label, "SINSTI") == 0) {
      val = strToUL(elem1);
      if (val != -1) teleinfo.SINSTI = (unsigned int)val;
      else return -1;
   }
}

int main()
{
    //freopen("out.txt", "a", stdout);
    int fd;
    int wlen;
    unsigned char line[MAX_LINE_SIZE];
    unsigned int linelen = 0;
    unsigned int pos = 0;
    fd = open(SERIAL_PORT, O_RDONLY | O_NOCTTY | O_SYNC);
    if (fd < 0)
    {
        printf("Error opening %s: %s\n", SERIAL_PORT, strerror(errno));
        return -1;
    }

   /* Get current time */
   gettimeofday(&tv1, &tz1);
   gettimeofday(&tv2, &tz2);

    /* Open serial port */
    set_interface_attribs(fd, BAUD_RATE);

    /* Receive characters */
    do
    {
        unsigned char buf[BUFFER_SIZE];
        int rdlen;
        int first_line = 1;

        rdlen = read(fd, buf, sizeof(buf) - 1);
        if (rdlen > 0)
        {
         int i = 0;
         while (i < rdlen)
         {
            // Discard all special characters
            const char c = buf[i];
            if (((c >= 0x00) && (c <= 0x01)) || ((c >= 0x04) && (c <= 0x08)))
            {
               //printf("DEBUG --> Char 0x%x discarded in line: \"%s\"\n", c, line);
               i++;
               continue;
            }

            // Get character
            line[pos] = c;

            // If line ending is found
            if (((c >= 0x02) && (c <= 0x03)) || ((c >= 0x0a) && (c <= 0x0f)))
            {
               // Close string
               line[pos] = 0;
               linelen = pos;
               pos = 0;

               // Discard first line as it may be truncated
               if (first_line)
               {
                  first_line = 0;
               }
               // Discard lines too small
               else if (linelen < MIN_LINE_SIZE)
               {
                  // Nothing to do
                        //printf("WARNING --> Line discarded because too small: \"%s\"\n", line);
               }
               // Discard incoherent lines
               else if (line[linelen-2] != DEMILITER)
               {
                  //printf("ERROR --> Ending delimiter not found on line: \"%s\"\n", line);
               }
               else
               {
#ifdef DISPLAY_LINE_STRING
                  //printf("Read %d: \"%s\"\n", linelen, line);
#endif
#ifdef DISPLAY_LINE_HEX
                  unsigned char *p;
                  //printf("Read %d:", linelen);
                  for (p = line; linelen-- > 0; p++)
                     //printf(" 0x%x", *p);
                  //printf("\n");
#endif
                  // Verify checksum
                  if (isCheckSumOk(line) == 0)
                  {
                     //printf("SUCCESS --> Checksum OK on line: \"%s\"\n", line);
                     processLine(line);
                  }
               }
            }

            else if (pos == sizeof(line) - 1)
            {
               // Close string
               line[pos] = 0;
               linelen = pos;
               pos = 0;

               printf("ERROR: no new line read in line: \"%s\"\n", line);
            }

            else if (pos == sizeof(buf) - 1)
            {
               // Close string
               line[pos] = 0;
               linelen = pos;
               pos = 0;

               printf("ERROR: no new line read in buffer: \"%s\"\n", buf);
            }

            else
            {
               pos++;
            }

            i++;
         }

#ifdef DISPLAY_BUFFER_STRING
            buf[rdlen] = 0;
            printf("Read %d: \"%s\"\n", rdlen, buf);
#endif
#ifdef DISPLAY_BUFFER_HEX
            unsigned char *p;
            printf("Read %d:", rdlen);
            for (p = buf; rdlen-- > 0; p++)
                printf(" 0x%x", *p);
            printf("\n");
#endif
        }
        else if (rdlen < 0)
        {
            printf("Error from read: %d: %s\n", rdlen, strerror(errno));
        }


      /* Check if it's time to send a message to Domoticz */
      if (send_message)
      {
         send_message = 0;

         //Calculate values to send
         //int SINST = (int)(teleinfo.SINSTS) - (int)(teleinfo.SINSTI);
                        int SINST =  (int)(teleinfo.SINSTI);
         char SINST_str[12]; sprintf(SINST_str, "%d", SINST);

         //int EAT = (int)(teleinfo.EAST) - (int)(teleinfo.EAIT);
                        int EAT =  (int)(teleinfo.EAIT);
         char EAT_str[12]; sprintf(EAT_str, "%d", EAT);

         //Construction of message to send
         char message [1000];
         strcpy(message, JSON_HEADER);
         strcat(message, SINST_str);
         strcat(message, ";");
         strcat(message, EAT_str);
         //Send message
         if(sendUrl(message) == 0)
         {
            printf("DEBUG --> New message sent: \"%s\"\n", message);
                                exit(0);
         }
      }
      else
      {
         gettimeofday(&tv2, &tz2);
         if((tv2.tv_sec - tv1.tv_sec) >= 10)
         {
            send_message = 1;
            tv1.tv_sec = tv2.tv_sec;
         }
      }

        /* sleep and repeat read */
        //sleep(1);
    } while (1);
}
Dernière modification par js-martin le 31 mars 2021, 18:53, modifié 2 fois.
Domotisation de : mes compteurs EDF, solaire, eau / mon alarme / ma Chaudière Viessamnn / mon congel / ma sonnette. Matériels : Pi2 - RFXTrx433e - Zwave+ Aeotec, ampoules Hue - Détecteur et prises Fibaro - Capteurs Oregon - présentation installation => lien
knasson
Messages : 245
Inscription : 26 mars 2021, 08:59

Re: teleinfo linky mode standard

Message par knasson »

Parfait! avec ta dernière version du code c'est nickel ! plus d'erreur !
je vois bien une remontée en KW dans le widget donc c'est déjà un grand pas, après je vais voir les détails..
grand merci a toi.. a charge de revanche.

cordialement,
jean-Claude
knasson
Messages : 245
Inscription : 26 mars 2021, 08:59

Re: teleinfo linky mode standard

Message par knasson »

si j'ai bien compris je lance le binaire toutes les 2 minutes avec crontab et ca marche!
je vais essayer. et je vous tiens au courant..
@
plus

Jean-claude
knasson
Messages : 245
Inscription : 26 mars 2021, 08:59

Re: teleinfo linky mode standard

Message par knasson »

"Apres MAJ 4,9 le totalisateur du compteur de production ne s'affiche pas.
Solution = mettre un compteur virtuel P1
6 parametres, deux utiles (tot;0;0;0;instant;0)

504999;0;0;0;999;0"


j'ai mis un compteur virtuel P1, par contre c'est quoi le code pour adresser ce compteur?
j'ai mis ca:
const char* JSON_HEADER = "http:// raspberrypi:8080/json.htm?/type=command&param=udevice&idx=10&nvalue=0&svalue=";
mais ca ne fonctionne pas!
merci de tes éclaircissements..

@ plus
knasson
js-martin
Messages : 487
Inscription : 22 mars 2015, 22:08
Contact :

Re: teleinfo linky mode standard

Message par js-martin »

Je ne sais plus ce que j'ai fait !

Je vois cela dans le code :

http://ip_pi:8080/json.htm?type=command&param=udevice&idx=735&nvalue=0&svalue="

mais j'ai deux widgets :
Capture.JPG
Capture.JPG (55.11 Kio) Consulté 6332 fois
Je me rappelle pourquoi j'en ai deux : cela me permet d'afficher la production instantanée ET le cumul jour sur la représentation graphique :
Capture.JPG
Capture.JPG (55.11 Kio) Consulté 6332 fois
Oui, tu fais la MAJ via la contab (moi j'ai mis 1 min) :

Code : Tout sélectionner

# toutes les 1 minutes, je mets à jour la production solaire
*/1 * * * * /home/pi/domoticz/scripts/maj_linky_prod >/dev/null 2>&1
Pièces jointes
Capture2.JPG
Capture2.JPG (205.99 Kio) Consulté 6332 fois
Domotisation de : mes compteurs EDF, solaire, eau / mon alarme / ma Chaudière Viessamnn / mon congel / ma sonnette. Matériels : Pi2 - RFXTrx433e - Zwave+ Aeotec, ampoules Hue - Détecteur et prises Fibaro - Capteurs Oregon - présentation installation => lien
knasson
Messages : 245
Inscription : 26 mars 2021, 08:59

Re: teleinfo linky mode standard

Message par knasson »

Sympa ta maison le toit est en pagode!
merci pour la photo...et je vais voir comment suivre tes traces...

cdlmt
Répondre