Pilotare delle resistenze in funzione della produzione FV

Premesso che so poco o nulla di programmazione, salvo capire un po' la lingua italiana e dedicare del tempo per imparare, C, C "semplificato" per Arduino ecc..

Ho dei pannelli solari FV, un boiler da 1500 lt d'acqua per il riscaldamento (a pavimento) che attualmente viene riscaldata (pochissimo) dai pannelli solari termici e (moltissimo) dalla caldaia a gas GPL.

Il progettino consiste nell'inserire due resistenze elettriche nel boiler, alimentarle con la corrente proveniente dai pannelli FV, e pilotare l'alimentazione di queste resistenze con un arduino.
Arduino dovrebbe prendere i valori di produzione del FV da un pc linux dedicato (a basso consumo, 6w) che è già attivo e funge egregiamente da dataloger, e che ha solo uscite usb ed Eth.
Linux (che monta Archilinux) archivia dei files csv e txt. Devo scrivere qualche routine che invia il valore numerico sulla usb collegata ad Arduino Uno i valori medi ultimi in ordine di tempo, scaricati dall'inverter in tempo reale via RS485.
A questo punto Arduino, a seconda del segnale che arriva da Linux (elaborato con algoritmo in funzione della produzione FV e della relativa convenienza economica), deve accendere una o più resistenze ad immersione nel boiler, tramite lo shield relay collegato.

Cosa ne pensate del progetto?

Sarà difficile scrivere il codice? Mah, mi ricorda tanto quando mi regalarono il Meccano, a 7 anni... XD

Ciao, Salvatore

Se interpreto ben Tu hai panelli FV e produci corrente continua o hai un inverter che Ti produce corrrente alternata 230V?
Ciao Uwe

A parte la giusta osservazione di Uwe relativa alla parte prettamente hardware, il progetto è fattibilissimo.
Devi scriverti un software lato Linux (a proposito, credo tu ti riferisca alla distribuzione ArchLinux, non mi pare ne esista una che si chiama ArchiLinux) che spedisce via USB (si tratta di una comunicazione seriale) i dati all'Arduino.

Puoi anche delegare il compito di elaborazione all'Arduino stesso: esso non è una scheda passiva ma contiene un microcontrollore, ossia una specie di microcomputer capace di elaborare dati in proprio e pilotare in modo indipedente le tue resistenze. Quindi dalla tua Linux box puoi inviare i soli comandi rendendo l'Arduino di fatto un semplice attuatore come anche spedire i dati letti dal pannello e far calcolare gli interventi di commutazione alla scheda stessa.

@Uwe: ho un impianto fotovoltaico sul tetto da 4,2 Kw, due stringhe, un inverter, regime di scambio sul posto.
Quindi la corrente che uso è alternata 220 v, la normale corrente di rete monofase fornita da Enel.

Preciso meglio lo stato del progetto.

Lato Arduino:

  • il programma sarà compilato in modo che la porta seriale usb stia sempre in ascolto, quando legge valore "x" attiva il relè "A", quando arriva il valore "y" eccita il relè "B", al valore "z" li attiva tutte e due, quando legge il valore "0" non attiva nessun relè.
    E questa credo che sia la parte più semplice del programma. Di sicuro trovo progetti all'interno dei quali è già descritta parte di questa procedura. Ma siccome non conosco ancora il C, vi sono grato se mi li indicate, almeno dove, in quale repository andare a cercare...

  • Per la seconda parte del progetto credo che invece la salita si faccia un po' più dura (almeno per me).
    Infatti intendo monitorare le fasi attacco/stacco delle resistenze/relè, con la finalità di gestirle in modo economicamente conveniente, e creare così i presupposti di un algoritmo un po' complesso, che tenga conto di diverse variabili, quali curva di rendimento del processo al variare dell'insolazione, convenienza economica dei diversi sistemi di riscaldamento, ecc... Ma procediamo step by step e poniamo il primo traguardo:
    a. creare un datalog in files testo col tempo/orari di accensione dei singoli relè/resistenze.
    Per questo si può optare per (almeno) due direzioni:
    a.1) restituire questi valori sulla porta (la stessa?) usb che comunica con linux. In questo caso ci penserà poi un programma su linux a fare da datalog.
    a.2) realizzare il datalog su una memoria sd all'interno dello shield eth (che ho preso, in previsione di questo tipo di funzione), per poi andare a interrogare/interfacciare questo serverino http con altri sistemi presenti in Lan (o anche wan, dipende).

Quindi ripeto, cosa ne pensate?

Tnx, per l'attenzione (e la pazienza per il lungo 3d)

Salvatore

leo72:
... (a proposito, credo tu ti riferisca alla distribuzione ArchLinux, non mi pare ne esista una che si chiama ArchiLinux) ...

Ahi ahi, qui siamo precisi, eh? Subito matita rossoblu! XD
Si, l'osservazione è giusta, tra l'altro stavamo scrivendo in contemporanea :slight_smile:

link a progetti (o parte di essi)?

Ciao

salvato:
E questa credo che sia la parte più semplice del programma. Di sicuro trovo progetti all'interno dei quali è già descritta parte di questa procedura. Ma siccome non conosco ancora il C, vi sono grato se mi li indicate, almeno dove, in quale repository andare a cercare...

Arduino non è una distro con un repo da cui scaricare i programmi :wink:
Devi cercare progetti già fatti e condivisi sia qui sul forum che su internet, c'è veramente di tutto a giro.

  • Per la seconda parte del progetto credo che invece la salita si faccia un po' più dura (almeno per me).
    Infatti intendo monitorare le fasi attacco/stacco delle resistenze/relè, con la finalità di gestirle in modo economicamente conveniente, e creare così i presupposti di un algoritmo un po' complesso, che tenga conto di diverse variabili, quali curva di rendimento del processo al variare dell'insolazione, convenienza economica dei diversi sistemi di riscaldamento, ecc...

Qui esistono formule e modelli matematici ma non entro nel discorso perché sono assolutamente ignorante in materia. Attendiamo l'intervento di qualcun altro.

Ma procediamo step by step e poniamo il primo traguardo:
a. creare un datalog in files testo col tempo/orari di accensione dei singoli relè/resistenze.
Per questo si può optare per (almeno) due direzioni:
a.1) restituire questi valori sulla porta (la stessa?) usb che comunica con linux. In questo caso ci penserà poi un programma su linux a fare da datalog.

Questo è facile, la comunicazione seriale è bidirezionale per cui puoi spedire dati all'Arduino come riceverli da esso.

a.2) realizzare il datalog su una memoria sd all'interno dello shield eth (che ho preso, in previsione di questo tipo di funzione), per poi andare a interrogare/interfacciare questo serverino http con altri sistemi presenti in Lan (o anche wan, dipende).

Meglio la prima soluzione, visto che hai già un minicomputer a cui agganci l'Arduino.

salvato:

leo72:
... (a proposito, credo tu ti riferisca alla distribuzione ArchLinux, non mi pare ne esista una che si chiama ArchiLinux) ...

Ahi ahi, qui siamo precisi, eh? Subito matita rossoblu! XD
Si, l'osservazione è giusta, tra l'altro stavamo scrivendo in contemporanea :slight_smile:

Non era una bacchettatura, solo una precisazione. PS: qui siamo in 2 ad usare Arch Linux, io e l'utente lesto.

Per Leo72 e Lesto

/* breve ot (ma non troppo fuori dal seminato):

il seguente sorgente, in C, legge un valore numerico da un file .csv, compreso tra 1 e 4500.
Non riesco a compilare la parte che mi fa il print sulla seriale usb del minipc nel quale gira Arch Linux...
Tra l'altro poi Arduino mi deve leggere il valore per intero e non come sequenza dei valori dei caratteri...

Ciao, Salvatore

fine ot */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <cstdio.h> // libreria per il print su serial port

//=============

int mtomm (char *month);

//............

char dir_in[100] = "E:\\Steve\\Personale\\Data\\";
char nome_file[200];
char riga[200];
char tempo_attuale[20];

int i, col;

float valore;

char car;

char data[9], ora[5];

int gg, mm, aa;
int hh, min, sec;

const int TEST = 1;

int main ()
{

FILE *fdata;

// 1 open file data
sprintf(nome_file,"%s20121116.csv", dir_in);
//printf("\nTEST \nfile personale \n%s \n", nome_file);

if (TEST == 1)
    {
    printf("\n****\n\ntest mode start\n\n****\n\n");

    printf("The current filename is: %s\n\n", nome_file);

     };

fdata=fopen(nome_file,"r");
if (fdata==NULL) { printf("\n ***** Nome file dati non valido \n %s \n", nome_file); exit(0); };


//printf("\n TEST LETTURA FILE PERSONALE \n");
//sprintf(nome_file,"%s_personale_letto.txt", dir_out);
//fc=fopen(nome_file,"w");
//if (fc==NULL) { printf("\n ***** Nome file letto personale non valido \n %s \n", nome_file); exit(0); };

time_t rawtime;

if (TEST == 1)
    {

    printf("The current rawtime is: %d\n\n", rawtime);

    fscanf(fdata, "%s", riga);

    printf("line one of file %s \n %s\n", nome_file, riga);
    };


i=0;

while( !feof(fdata) ) {

    fscanf(fdata, "%s", riga);

    i++;

    }


if (TEST == 1)
    {
    printf("riga :  %d\n%s\n\n", i, riga);

    printf("colonna : %d\n%f\n\n", col, valore);

    };


if (TEST == 1)
    {
    printf("prima di sscanf\n");
    printf("riga :  %d\ncontenuto riga :%s\n\n", i, riga);

//    printf("colonna : %d\n%f\n\n", col, valore);

    };


for (col=1; col<11; col++)
    {

    if (TEST == 1)
        {

        printf("\ncol : %d riga: %s\n\n", col, riga);

//        printf("valore :  %f\t", valore);

        };

    sscanf(riga, "%[^','],%s,%[^','],%s",  nome_file, riga);
    };

valore = atof(nome_file);

if (TEST == 1)
    {

    printf("dopo sscanf\nriga :  %d\ncontenuto riga : %s\ncontenuto nome_file : %s\n\n", i, riga, nome_file);

    printf("valore :  %f\t", valore);

    };


fclose(fdata);
//serial.print (valore) /dev/tty.usbmodem1d11  //stampa invia il valore &d su seriale

if (TEST == 1)
    {
    printf("\n****\n\ntest mode ended\n\n****\n\n");

    };

//system("PAUSE");
return 0;
}
fdata=fopen(nome_file,"r");
if (fdata==NULL) { printf("\n ***** Nome file dati non valido \n %s \n", nome_file); exit(0); };

Il codice sopra devi scriverlo così:

fdata=fopen(nome_file,"r");
if (fdata==NULL) 
{ 
    printf("\n ***** Nome file dati non valido \n %s \n", nome_file); exit(0); 
} // noterai la mancanza del ; dopo la parentesi }

In genere il codice interno a tutti i blocchi {} deve essere identato di almeno 4 caratteri o un tab (che di solito e di 8)
main() è una funzione. Il codice dentro la funzione è un blocco di codice delimitato da {} e va identato come detto sopra.

if (TEST == 1)
    {
    printf("\n****\n\ntest mode start\n\n****\n\n");

    printf("The current filename is: %s\n\n", nome_file);

     };

Deve essere scritto così:

if (TEST == 1)
{
    printf("\n****\n\ntest mode start\n\n****\n\n");
    printf("The current filename is: %s\n\n", nome_file);
}

Copia l'errore qui, che vediamo se c'è altro.

Ciao.

Ciao Mauro,

grazie per la risposta, ma il problema di questo sorgente è che non riesco a trovare l'istruzione corretta per inviare il dato sulla seriale usb.

(dopo di che avrò il problema di compilare il sorgente in C per la distro ArchLinux, ma questo, a parte il corretto individuare la porta seriale, non credo che sia un problema... )

e, lato Arduino, ricomporre l'array di caratteri per passarli come numero intero di più cifre e non come singoli char...

Ciao, Salvatore

Domanda: Non puoi installare python e scrivere un programma in python?

Comunque in C (in unix ancora meglio) i dispositivi appaiono come dei file e si trovano sotto /dev, le seriali pure o no cominciano con tty, nel caso di semplice convertitore usb seriale tipo Arduino UNO il device si chiama /dev/ttyACM0.

Visto che i device sono file, si posso aprire e chiudere con open(), ma visto che sono dei file particolari ci sono altre funzioni per impostare la seriale.

Prova questo codice e dimmi se lavora.

#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <sys/fcntl.h>
 
 
int main( int argc, char *argv[] )
{
	struct termios termOptions;
	char        port[1024];
	int         ttyFid;
 
	if( argc == 2 )
	{
		// Copy the port from the argument
		strcpy( port, argv[1] );
		printf( "Read port name: %s\n", port );
	}
	else
	{
		// No argument or incorrect number of arguments read:
		printf( "Usage: setSer /dev/ttySX\n" );
		return -1;
	}
 
	// Open the tty:
	ttyFid = open( port, O_RDWR );
	if (ttyFid == -1)
	{
		printf( "Error unable to open port: %s\n", port );
		return -1;
	}
 
	// Get the current options:
	tcgetattr( ttyFid, &termOptions );
	
	// Set the input/output speed to 921.6kbps
	cfsetispeed( &termOptions, B921600 );
	cfsetospeed( &termOptions, B921600 );
 
	// Now set the term options (set immediately)
	tcsetattr( ttyFid, TCSANOW, &termOptions );
 
	// All done
	printf( "Done, port speed set on: %s\n", port );
}

Ciao.

Grazie Mauro, quando torno a casa lo provo. Purtroppo dall'ufficio ultimamente non riesco ad entrare nella mia lan domestica con ssh. Si deve essere bloccato qualche servizio sul router o sul minipc nel quale sta girando ArchLinux (è un data logger di un impianto fotovoltaico...).
Ovviamente si intende che il sorgente che hai postato è lato pc, non lato Arduino, a scanso di equivoci... :slight_smile:

sm

rispondo quì.

char dir_in[100] = "E:\\Steve\\Personale\\Data\\";

già quà qualcosa non mi torna. questo è un path windows, non unix...

poi sarà che mi perdo tra l'identazione "a cazzum" e le verie righe commentate, ma non mi pare di vedere nulla che scrive sulla seriale...

vorrei sapere se su questa versione di linux ho problemi particolari legati a differenze di sintassi, di puntamento alle periferiche o di compilazione

assolutamente no, anzi.. il linguaggio è uno (anzi più) standard del compilatore, la gestione delle periferiche è standard derivata da kernel e driver in uso, l'unica cosa che può variare sono i path delle periferiche ma in tal caso basta fare un file di configurazione per UDEV, se guardi come installare arduino su linux, la sezione "the hard way", trovi tutta la spiegazione di come creare le regole. MA stiamo divagando, quello NON è il tuo problema, sarebbe bello avere l'errore di compilazione

Tra l'altro poi Arduino mi deve leggere il valore per intero e non come sequenza dei valori dei caratteri...

ah, la seriale "parla" carattere per carattere, quindi sei tu che lato arduino devi ricomporre la stringa di partenza. Io di solito per fare in fretta uso un formato tipo
*stringa da inviare#
quindi se leggo * cancello querllo che ho letto fin'ora (buffer) e setto una flag (esempio ok = true), se leggo # e la flag è true, elaboro il contenuto del buffer e setto la flag a false, altrimenti ignoro il tutto (al messaggio manca un pezzo), qualsiasi altro carattere va a finire nel buffer.

edit: il vantaggio di quetso metodo è che puoi debuggarlo/leggere i valori in modo umano da serial monitor i metodi seguenti invece a serial monitor mandano una serie di simboli strani, dai un'occhiata alla tabella ascii per capire i valori inviati, ricavarne il binario, unire i 2 byte e convertire in numero leggibile da un umano.

nel tuo caso, se invii solo numeri interi, volendo ottimizzare, possiamo pensare in byte. Sappiamo che un int è composto da 2 byte su arduino; esempio:
byte0 = *
byte1 = primo byte del numero
byte2 = secondo byte del numero

però * in byte è un numero anche lui! come risolvere questo conflitto (anche byte 1 e 2 potrebbero avere lo stesso valore di *!). In tal caso uso un sistema "a spanne" o "a ritardo".
"a spanne": leggo tanti byte, facciamo una ventina, e controllo che la presenza del valore corrispondente a * sia ogni 3 byte a partire dai primi 3 byte. Al che sorgono i casi:

  1. nessuna corrsipondenza: qualcosa sta andando storto. Baudrate errati?
  2. una sola corrispondenza: allora il byte corrispondente è l'inizio, ora sai da dove iniziare a leggere
  3. più di una corrispondeza: in tal caso o byte1 o byte2 per puro caso continuano ad avere il valore di * e quindi sono indistinguibili dall'inizio. Leggi altri 20 valori e ricomincia.

"a ritardo":
lo " scrittore" aspetta un segnale di inizio da parte del "lettore"; in tal caso siamo sicuri che i byte letti siano in sequenza e diamo per scontato che non perdiamo dati. volendo possiamo anche fare a meno del byte0, riducendo di 1/3 la banda necessaria!
La richiesta può essere fatta solo una volta nel setup o ad ogni lettura.

questo è il sistema migliore in termini di banda, se la richiesta è fatta solo una volta, ma richiede di portare sia tx che rx, in oltre richiede di resettare sia arduino che il programma PC ogni volta che modifichi una delle due parti, ed è sensibile agli errori (perdita di un byte)

lesto:
rispondo quì.

char dir_in[100] = "E:\\Steve\\Personale\\Data\\";

già quà qualcosa non mi torna. questo è un path windows, non unix...

Si Lesto, in effetti sono abbastanza multipiattaforma, visto che normalmente uso mac, in ufficio winxp, per diletto (poco) linux. In particolare poi ho un minipc (model Pogoplug riadattato) con ArchLinux come datalog per l'impianto FV.
Il sorgente che hai visto è stato scritto su winxp, di qui il path...

/edit
perché non usare stream .read() o leggere i char come arrais e convertirli?
Forse ho detto una castroneria....eh?

poi sarà che mi perdo tra l'identazione "a cazzum" e le verie righe commentate, ma non mi pare di vedere nulla che scrive sulla seriale...

Si, in effetti l'indentazione è parzialmente alla cdc, a causa dei continui pasticci/tentativi di far girare il pezzo...

MA stiamo divagando, quello NON è il tuo problema, sarebbe bello avere l'errore di compilazione

Beh, non ho ancora un vero messaggio di errore in debug, nel senso che sto cercando di capire la corretta sintassi...

..... due parti, ed è sensibile agli errori (perdita di un byte)

Su questo torno dopo che ho studiato quanto hai scritto :fearful:

Per ora many thanks

Salva

perché non usare stream .read() o leggere i char come arrais e convertirli?

e come fa il magico .read() a sapere DOVE inizia e finisce la stringa? dobbiamo creare un protocollo (nel post precedente ti ho elencato una serie di protocolli che mi sono inventato per inviare i dati, quindi a parte il parolone nulla di trascendentale... se non ne hai bisogno :slight_smile: )
E visto che non esiste un protocollo standard riconosciuto universalmente, e che l'implementazione tutto sommato è un ottimo esercizio per iniziare, viene lasciato come compito a casa all'utente :wink:

azz... è proprio tempo di compiti a casa... :smiley:
Ok, torno online quando ho qualcosa da correggere...
/(anche se, a dir la verità non riesco a capire la sintassi dell'esempio * istruzione # ma domani mi ci dedico.../

ciao

ecco fai attenzione. normalmente arduino stampa sempre nel loop, e ogni messaggio normalmente è di più lettere. Quindi nel momento in cui lanci il programma "ascoltatore" lato PC, chi ti assicura che non ti sei perso un pezzo messaggio?

Certo potresti tenere prenuto il tasto reset finchè non lanci il programma ascoltatore, ma ne vale la pena ogni volta? bhe magari all'inizio sì, poi ti sarà scomodo, sopratutto quando avrai bisogno di lavorqarci su spesso