Comandare ventola PWM con LIN BUS

Ciao a tutti,
sono un novizio di Arduino e non ho nessuna base di informatica. Ho bisogno di un grosso aiuto.
Devo comandare una ventola PWM leggendo i comandi da una rete LIN Bus.
Ho trovato qui sul forum un codice che legge la LIN BUS, ho identificato i numeri che corrispondono alla velocità della ventola e il codice che fa girare le ventole a 3 velocità.

Dovrei mettere assieme le due cose:

-quando legge sulla LIN il n° 76 comandare la ventola al 75% PWM
-quando legge sulla LIN il n° 75 comandare la ventola al 50% PWM
-quando legge sulla LIN il n° 74 comandare la ventola al 25% PWM
-quando legge sulla LIN il n° 7F la ventola è ferma

Il codice originale che utilizzo per leggere la LIN con un Arduino DUE l'ho preso dalla discussione "LIN Bus communication Uno to Due".
Il codice per comandare la ventola l'ho preso dall'esempio della libreria "pwm01.h".
Di seguito il codice ibrido che ho messo assieme con scarsi risultati. Come ho anticipato non ho basi di informatica.

//  CODICE PER RILEVAZIONE EVENTI SU LINBUS e COMANDO VENTOLE
//Due project, Receive

#include <SerialSniffer.h>
#include <lin_stack.h>
#include <pwm01.h>

const int PIN_TXE = 18;
const int PIN_CS = 2;
word VentPin = 3;

uint8_t rxByte[4];

void setup() {

  pinMode(PIN_CS, OUTPUT);
  pinMode(VentPin, OUTPUT);
  digitalWrite(PIN_CS, HIGH);
  pwm25kHzBegin();

  Serial1.begin(9600, SERIAL_8E1); //LIN Serial Rx, 8E1 is BMW iBus standard
  //Serial.begin(9600); //debug serial
  //Serial.println("Due debug Comms");

  delay(100);
}

void loop() {
  while (Serial1.available()) {
    delay(5);
    byte actuallyRead = Serial1.readBytes(rxByte, 8); //Read UP TO 8 bytes
    if (actuallyRead > 0)
    {
      for (uint8_t i = 0; i < actuallyRead; i++)
      {
        Serial.println(rxByte[i], HEX);
      }
    }
    if (rxByte == 74)
    { pwmDuty(59); // 75% (range = 0-79 = 1.25-100%)
      delay (5000);
    }
    if (rxByte == 75)
    { pwmDuty(39); // 50% (range = 0-79 = 1.25-100%)
    delay (5000);
    }
    if (rxByte == 76)
    { pwmDuty(19); // 25% (range = 0-79 = 1.25-100%)
    delay (5000);
    }
    
  }

  void loop() {
    pwmDuty(19); // 25% (range = 0-79 = 1.25-100%)
    delay(5000);
    pwmDuty(39); // 50% (range = 0-79 = 1.25-100%)
    delay (5000);
    pwmDuty(59); // 75% (range = 0-79 = 1.25-100%)
    delay (5000);
  }

  void pwm25kHzBegin() {
    TCCR2A = 0;                               // TC2 Control Register A
    TCCR2B = 0;                               // TC2 Control Register B
    TIMSK2 = 0;                               // TC2 Interrupt Mask Register
    TIFR2 = 0;                                // TC2 Interrupt Flag Register
    TCCR2A |= (1 << COM2B1) | (1 << WGM21) | (1 << WGM20);  // OC2B cleared/set on match when up/down counting, fast PWM
    TCCR2B |= (1 << WGM22) | (1 << CS21);     // prescaler 8
    OCR2A = 79;                               // TOP overflow value (Hz)
    OCR2B = 0;
  }

  void pwmDuty(byte ocrb) {
    OCR2B = ocrb;                             // PWM Width (duty)
  }

... beh ... usa una semplice struttura switch/case e assegni il valore del PWM in funzione del valore LIN ricevuto :slight_smile:

Guglielmo

Grazie Guglielmo!
Sembra perfetta per quello che devo fare.
Questa sera provo.

Mauro

>PM68: ti ricordo che in conformità al regolamento, punto 7, devi editare il tuo post (quindi NON scrivendo un nuovo post, ma utilizzando il bottone More -> Modify che si trova in basso a destra del tuo post) e racchiudere il codice all'interno dei tag CODE (... sono quelli che in edit inserisce il bottone con icona fatta così: </>, tutto a sinistra).

In pratica, tutto il tuo codice dovrà trovarsi racchiuso tra due tag: [code] _il _tuo_ codice_ [/code] così da non venire interpretato e non dare adito alla formazione di caratteri indesiderati o cattiva formattazione del testo. Grazie.

Guglielmo

Grazie Guglielmo per le istruzioni.
Avevo cercato di inserire il codice correttamente ma non ci sono riuscito.
Ora ci riprovo.

Ho modificato il codice inserendo la struttura switch case come mi hai suggerito però mi da questo errore che mi sembra avere capito dipende dalla costante inByte che ho provato a mettere "char" invece di "int" ma non gli piace:

jump to case label [-fpermissive]

Il mio codice è questo:

//  Arduino DUE
//  CODICE PER RILEVAZIONE EVENTI SU LINBUS  e COMANDO VENTOLE

#include <SerialSniffer.h>
#include <lin_stack.h>
#include <DuePWM.h>

#define PWM_FREQ1  20000
#define PWM_FREQ2  20000

DuePWM pwm( PWM_FREQ1, PWM_FREQ2 );
#define PIN_TXE 1
#define PIN_CS 2
#define FAULT_PIN 9
uint8_t rxByte[4];

void setup() {
  pinMode(PIN_CS, OUTPUT);
  pinMode(FAULT_PIN, OUTPUT);
  digitalWrite(PIN_CS, HIGH);
  digitalWrite(FAULT_PIN, HIGH);
  Serial1.begin(9600, SERIAL_8E1); //LIN Serial Rx, 8E1 is BMW iBus standard
  pwm.setFreq1( PWM_FREQ1 );
  pwm.setFreq2( PWM_FREQ2 );
  pwm.pinFreq1( 6 );  // Pin 6 freq set to "pwm_freq1" on clock A
  pwm.pinFreq2( 7 );  // Pin 7 freq set to "pwm_freq2" on clock B
}

void loop() {
  if (Serial1.available() > 0) {
    char inByte = Serial1.read();

    switch (inByte) {
      case '74': //quando legge "74" sulla serial1
        uint32_t pwm_duty = 190; // 75% duty cycle
        pwm.pinDuty( 6, pwm_duty );  // 75% duty cycle on Pin 12
        break;
      case '75': //quando legge "75" sulla serial1
        uint32_t pwm_duty = 127; // 50% duty cycle
        pwm.pinDuty( 6, pwm_duty );  // 50% duty cycle on Pin 12
        break;
      case '76': //quando legge "76" sulla serial1
        uint32_t pwm_duty = 63; // 25% duty cycle
        pwm.pinDuty( 6, pwm_duty );  // 25% duty cycle on Pin 12
        break;
      case '7F': //quando legge "7F" sulla serial1
        pwm.stop(6);
        break;

    }
  }
}

Ho risolto, mancavano alcune parentesi graffe.

Mmm ... sicuro di quei "case" ? ? ?

inByte conterrà, dopo la lettura dalla seriale 1 byte (1 carattere) mentre tu stai facendo dei "case" con un insieme di due caratteri (es. '7F', ovvero un numero a 16 bit).

Ho invece idea che tu devi confrontare, visto i numeri che indichi, con valori esadecimali, che si rappresentano con lo 0x davanti ovvero 0x74, 0x75, 0x76 e 0x7F che occupano 1 byte (1 carattere) come tu ricevi.

Guglielmo

Grazie di tutti i consigli Guglielmo, ho corretto.
Ora volevo provarlo dandogli i comandi da monitor seriale, ho cambiato da "Serial1" a "Serial" , non mi da nessun errore di compilazione, però la ventola parte alla massima velocità e qualsiasi cosa scrivo sul monitor seriale non cambia niente. Ho provato a scrivere sia 74 che 0x74 o 7F o 0x7F inoltre essendo di default "pwm.stop" mi aspetto che se non do comandi dovrebbe stare ferma.
Mi puoi dire cosa sbaglio?

#include <DuePWM.h>

#define PWM_FREQ1  20000
#define PWM_FREQ2  20000
char inByte ;

DuePWM pwm( PWM_FREQ1, PWM_FREQ2 );

void setup() {

  Serial.begin(9600);
  pwm.setFreq1( PWM_FREQ1 );
  pwm.setFreq2( PWM_FREQ2 );
  pwm.pinFreq1( 6 );  // Pin 6 freq set to "pwm_freq1" on clock A
  pwm.pinFreq2( 7 );  // Pin 7 freq set to "pwm_freq2" on clock B
}

void loop()
{
  if (Serial.available() > 0)
  {
    inByte = Serial.read();
    switch (inByte)
    {
      case 0x74: //quando legge "74" sulla serial1
        { uint32_t pwm_duty = 190; // 75% duty cycle
          pwm.pinDuty( 6, pwm_duty );  // 75% duty cycle on Pin 6
          delay (5000);
          break;
        }
      case 0x75: //quando legge "75" sulla serial1
        { uint32_t pwm_duty = 127; // 50% duty cycle
          pwm.pinDuty( 6, pwm_duty );  // 50% duty cycle on Pin 6
          delay (5000);
          break;
        }
      case 0x76: //quando legge "76" sulla serial1
        { uint32_t pwm_duty = 63; // 25% duty cycle
          pwm.pinDuty( 6, pwm_duty );  // 25% duty cycle on Pin 6
          delay (5000);
          break;
        }
      case 0x7F:
        { pwm.stop( 6 );
          delay (5000);
          break;
        }

      default:
        {
          pwm.stop( 6 );
        }
    }
  }
}

Che semplicemente NON puoi usare il monitor seriale ...
... il monitor seriale invia quello che scrivi in ASCII e qiuindi, se scrivi 74, NON invia il codice 0x74, ma invia DUE caratteri ASCII uno per il 7 che è 0x37 ed uno per il 4, che è 0x34. Idem per il resto.

Se vuoi usare il monitor seriale, devi temporaneamente modificare il codice è mettere, al posto di quei codici esadecimali, ad esempio delle lettere ... 'A', 'B', 'C', 'D' ... così quando premi una lettera viene inviata solo quella e riesci a leggerla :wink:

Guglielmo

Sei un grande Guglielmo! Con la seriale funziona.

Ora lo provo nella vecchia versione collegato alla LIN dell'auto.

Grazie
Mauro

Buongiorno a tutti,
proseguendo con i miei rilievi mi sono accorto che i frame che rilevavo sulla lin bus non erano completi perchè la velocità di trasmissione non è 9600 ma 19200.
Corretto il tutto i codici che attivano le ventole sono formati da più byte e inseriti nel mio codice mi da un errore di sintassi:"expected ':' before numeric constant".
Mi potete aiutare per la scrittura corretta? Di seguito il mio codice:

//  CODICE PER RILEVAZIONE EVENTI SU LINBUS LEXUS e COMANDO VENTOLE PWM

#include <DuePWM.h>

#define PIN_TXE 18
#define PIN_CS 2
uint8_t rxByte[8];

#define PWM_FREQ1  20000
#define PWM_FREQ2  20000
uint32_t pwm_duty = 0;
byte inByte;

DuePWM pwm( PWM_FREQ1, PWM_FREQ2 );

void setup() {
  pinMode(PIN_TXE, OUTPUT);
  pinMode(PIN_CS, OUTPUT);
  digitalWrite(PIN_TXE, HIGH);
  digitalWrite(PIN_CS, HIGH);
  Serial1.begin(19200, SERIAL_8E1); //LIN Serial Rx, 8E1 is BMW iBus standard
  pwm.setFreq1( PWM_FREQ1 );
  pwm.setFreq2( PWM_FREQ2 );
  pwm.pinFreq1( 6 );  // Pin 6 freq set to "pwm_freq1" on clock A
  pwm.pinFreq2( 7 );  // Pin 7 freq set to "pwm_freq2" on clock B
}

void loop()
{
  if (Serial1.available() > 0)
  {
    inByte = Serial.read();
    switch (inByte)
    {
      case 0x0 0x60 0xFE 0x0: //quando legge la sequenza "0 60 FE 0" sulla seriale
        { pwm_duty = 60; // 75% duty cycle
          pwm.pinDuty( 6, pwm_duty );  // 75% duty cycle on Pin 6
          break;
        }
      case 0x0 0x66 0xFE 0x0: //quando legge la sequenza "0 66 FE 0" sulla seriale
        { pwm_duty = 130; // 50% duty cycle
          pwm.pinDuty( 6, pwm_duty );  // 50% duty cycle on Pin 6
          break;
        }
      case 0x0 0x78 0xFE 0x0: //quando legge la sequenza "0 78 FE 0" sulla seriale
        { pwm_duty = 200; // 25% duty cycle
          pwm.pinDuty( 6, pwm_duty );  // 25% duty cycle on Pin 6
          break;
        }

      default:
        { pwm_duty = 255; // 0% duty cycle
          pwm.pinDuty( 6, pwm_duty );  // 0% duty cycle on Pin 6
          break;
        }
    }
  }
}

lo switch confronta 1 numero (int o char) quindi confronta il valore attuale di inByte che non può essere contemporaneamente 0 60 fe 0 che sono 4 byte
inoltre la sintassi del case non prevede quella scrittura

Il while/case vuole UNA sola costante numerica, quindi scordati una sitassi come quella che hai scritto.

Secondo me, la strada più semplice e rivecere dalla seriale in una "macchina a stati finiti" in cui avanzo nei vari "stati" mano mano che ricevo i byte corretti e, quando una sequenza è stata completamente identificata, faccio quello che devo fare.

Guglielmo

P.S.: Patrick_M mi ha parzialmente anticipato. :slight_smile:

Grazie del suggerimento, mi sono documentato su cosa vuol dire "macchina a stati finiti" e mi sono guardato parecchi esempi però sono bloccato.

Diciamo che il mio primo stato è il codice che rileva dalla Lin Bus una "sbrodolata" di byte concatenati. Qui sotto li ho incolonnati per chiarezza partendo dal 66 66 :

66 66 9E 0
66 66 78 1E 0 1E 0 E0 0 E0 0 0 FE E6 0 0 E0 0
66 66 E0 1E 0 6 60 80 18 18 60 86 0 0
66 66 9E 0 1E 0 0 0 0 0 0 E0 0
66 66 FE E6 0
66 66 6 0 0 0 0 0 0 60 FE 0 66 18 60 86 0
66 66 78 1E 0 1E 0 E0 0 E0 0 0 0 0 0 0 E0 FE 0
66 66 E0 1E 0 6 60 80 18 66 FE 0
66 66 9E 0 1E F8 0 0 0 0 0 0 E0 E0 0 0 FE E6 0
66 66 6 80 0 0 0 0 0 0 80 18 18 60 86 0
66 66 F8 78 1E 0 1E 0 E0 0 0 0 0 0 0 0 E0 0
66 66 1E 0 6 60 80 18 0 0 0 0 66 FE 0
66 66 9E 0 1E 0 0 0 0 0 1E 0 E0 0 E0 0 0 FE E6 0
66 66 6 98 0 1E 0 6 60 80 18 18 60 86 0
66 66 78 1E 0 1E 9E 0 1E 0 0 0 0 0 0 E0 0
66 66 E0 1E 0 6 80 0 0 0 0 0 0 66 FE 0

Nascosti tra questi byte ci sono le sequenze che mi interessano es. 0 60 FE 0 oppure 0 66 FE 0.

Il secondo stato penso dovrebbe essere rilevare il passaggio delle sequenze che mi interessano e salvarle in una variabile di stato ...ma non so come fare.
Mi potete aiutare?

Grazie in anticipo

Mi fai una grande cortesia?
riparti da capo con lo spiegare "cosa" vuoi fare, e non "come" lo vuoi fare?
qualche idea credo di averla, ma....

Certo!
La mia esigenza è di comandare due ventole PWM ricevendo i comandi da rete LinBus.
La rete Lin Bus esiste già.
Ho un codice in grado di leggere il traffico sulla rete che mi restituisce una "sbrodolata" di byte (un esempio lo puoi vedere sopra).
Analizzando il traffico sulla rete ho identificato i gruppi di byte che corrispondono ai comandi di funzionamento per le ventole che sono:

0 60 FE 0 = ventola SX alle velocità 3
0 66 FE 0 = ventola SX alle velocità 2
0 78 FE 0 = ventola SX alle velocità 1
0 F8 0 = ventola DX alle velocità 3
0 F8 FE 0 = ventola DX alle velocità 2
0 78 0 0 0 0 F8 FE 0 = ventola DX alle velocità 1

Quello che non so fare ora è impostare due variabili di stato che cambiano ogni qualvolta passa sulla rete una delle sequenze di comando indicate sopra.
Dallo stato di queste variabili farei dipendere il comando PWM alle ventole.

Grazie per il tuo interessamento

Stasera, dopo il lavoro, vedi di mettere insieme qualcosa
Se mi dai due dritte tu sullo hardware, perché la cosa interssa anche me

>PM68: ... come ti dicevo devi identificare i vari stati ... leggi da seriale, sei in stato 0, ogni volta che leggi uno ZERO te lo segni e verifichi se il successivo è ancora ZERO. Se si, rimani in tale stato, se NO, verifichi che sia 60, oppure 66, oppure 78, oppure F8 ... questo ti porta a 4 differenti stati, in ognuno di questi verifichi se il carattere successivamente ricevuto è quello giusto, es. sei nello stato in cui hai ricevuto 66, se ricevi FE è un carattere valido e passi allo stato successivo (ventola SX alle velocità 2), altrimenti NON è valido e torni allo stato ZERO.

E' lungo, ma con qualche trucchetto (es. uso di array per memorizzare le giuste sequenze), NON è difficile da realizzare.

In pratica è un "parser" che cerca determinate sequenze e lo fa in tempo reale.

Guglielmo

Alcune cose però non tornano
Linbus non lavora proprio così...

Comunque, stasera ci guardo e tento di darti due dritte