Software und Hardware für Solarflieger

Hallo Maccl
Das ist der Ukranduit-MPP, auch auf dem Attiny85. Ich finde es toll was er da macht, vor allem da er vielen Flugkollegen dort mit einer einfachen Lösung hilft. Der Attiny85 hat leider nur die 2x8Bit Timer. Die PWM ist dort 8Bit, 256Steps, da PPM 1-2mS davon die Hälfte, und die Randbereiche weg, ergeben sich etwa 100Regelschritte Auflösung, und der Arbeitspunkt wird manuel in der Software eingestellt. In dem ausgehendem Thread hatte ich eine 85er Soft gebastelt, 9Bit PWM, hat dann ca. 200Schritte Auflösung, und der MPP Arbeitspunkt ist vom Sender her per Gasknüppel wählbar.

Bei dem ÚSBtiny werkelt da ein Attiny44 mit 16Bit PWM, der die volle Auflösung des Reglers nutzt, die meisten Regler können 800Steps.


32u4 8MHz

Ich hatte gestern ein bissl Zeit, und mal mit dem 32u4 Hott inkl dem PPM.Passthrough gespielt, Hott funzt soweit, Uart ist nicht so mein Ding, das geht bestimmt auch besser (weniger Blähcode in der ISR), ich stelle das mal unausgegoren und nur teil kommentiert hier rein, die Tage teste ich das mal ausgiebig.
Diese Boards kosten auch um die 3-4€ in der Bucht, man kann den USB im "Vollbetrieb" weiternutzen, z.B. zum debug, oder evtl später Parameter per SerialTerm einzugeben, u.s.w. der 32u4 ist ungeahnter Luxus für mich, wenn nur diese blöde Arduino-IDE damit nicht wäre ;) (nur damit rennt der stabil, irgendwas scheine ich im C zu missachten).
Für die 3,3V Version sprechen auch 5mA statt 15mA Stromverbrauch.

Funktioniert auch ohne Hott, also quasi universell. Jeti u.s.w. habe ich keine Aktien dran, das müsste dann wer anders einbauen.
Wenn die Tage noch ein bissl Zeit finde, setze ich mich da noch bei den Feinheiten mal bei. Danach können ans sich schon die MPP-Routinen drauf.

Für den "Anwender" wäre es zumindest sehr einfach, Arduino-IDE installieren, UBS-Kabel einstöpseln, Code (Datei) öffnen, auf den Pfeil drücken, fettich :D
Ich würde es als "Universalboard" so in Raum werfen ?!? (Wir, also das Board und Ich, hatten anfangs so unsere gewissen Differenzen, sind mittlerweile aber dann doch noch Freunde geworden ;):D)

Code:
// Testroutine für das 8MHz 3,3V Arduinoboard mit dem 32u4 MCU. Nächster Schritt: Eingelesene PPM vom Empf. wird mit 500Hz an den
// Regler weitergeleitet, als Drehzahl im Hottsender angezeigt, und auf dem Arduino Serialmonitor ausgegeben.
// In der IDE bitte "Lilipad Arduino USB" benutzen. Der Code funktioniert ausschließlich auf einem 8MHz 32u4-Board ! 
// Regler -- D9   
// RC-Kanal -- D2 + D3 (beide!!)
// Hott-T -- D0(Rx) + über einen 1,2K Widerstand nach D1(Tx)

volatile uint8_t count;
volatile uint8_t HOTT[45] = {0x7C,0x8D,0x00,0xD0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,
                             0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7D,0x00};
void setup() { 
    UBRR1L = 0b00011001;  // HOTT-UART wird auf 19200baud konfiguriert          
    UCSR1B = 0b10011000;  // Hott-RX+TX Interruptsteuerung wird aktiviert
    TIMSK4 = 0b01000000;  // Hott-Delay-Timer Comperator A als ISR aktiviert  
    TCCR4B = 0b00000000;  // Hott-Delay-Timer stoppen, falls kein Hott verwendet wird
                  
    DDRB  |= 0b00100000;  // Read RC 
    EICRA  = 0b00001011;  // Read RC // INT1=falling INT0=rising
    EIMSK  = 0b00000011;  // Read RC
    TCCR3A = 0b00000000;  // Read RC
    TCCR3B = 0b00000010;  // Read RC
        
    TCCR1A = 0b10000010;  // PPM_OUT
    TCCR1B = 0b00011010;  // PPM_OUT
    ICR1   = 2000;        // PPM_OUT         
    sei();      
}
void loop() {   
  // Serial.begin(19200); Serial.println( HOTT[28]); Serial.end();   
  HOTT[28]++;  
}

ISR(INT0_vect){ TCNT3 = 0b00000000;}    // Read RC rising edge, der Timer wird genullt
ISR(INT1_vect){ OCR1A = TCNT3;     }    // Read RC falling edge, der Timer wird ausgewertet

// -----------HOTT-ISR-Routinen--------------------------//
ISR(USART1_RX_vect){
if  (UDR1   == 0b10001101)           // Hott-GAM hat nach Daten gerufen, Senderoutine wird gestartet:
{   UCSR1B   = 0b00001000;           // Hott-RX-Interrupt wird während der Senderoutine gesperrt,
    HOTT[44] = 0b00000000;           // Hott-Checksum auf Null stellen,
    count    = 0b00000000;           // Hott-Byte-Counter auf Null stellen,
    TCNT4    = 0b00000000;           // Hott-Delay-Timer auf Null stellen,
    OCR4A    = 0b10011110;           // Hott-Delay-Timer auf 5 mS stellen,
    TCCR4B   = 0b00001001;           // Hott-Delay-Timer starten.
}}
ISR(TIMER4_COMPA_vect){
if  (count   > 0b00101101)           // Wenn alle Bytes gesendet sind
{   TCCR4B   = 0b00000000;           // Hott-Delay-Timer stoppen
    UCSR1B   = 0b10011000;           // Hott-RX-Interrupt wieder aktivieren
}else{ 
    HOTT[44] = HOTT[44] + HOTT[count]; // Hott-Checksum bauen                 //while (!(UCSR1A & (1<<UDRE1))){}    
    UDR1     = HOTT[count];          // Hott-Byte aktuell schreiben
    TCNT4    = 0b00000000;           // Hott-Delay-Timer auf Null stellen
    OCR4A    = 0b00111111;           // Hott-Delay-Timer auf 2 mS stellen 
    count++;                
}}

Irgendwie werde ich das Bild unten im Beitrag nicht los :D??
 

Anhänge

  • photo_2018-11-08_13-50-15.jpg
    photo_2018-11-08_13-50-15.jpg
    79,9 KB · Aufrufe: 73
Kurzer Zwischenstand
Ein paar Sachen verändert, Rc auslesen über ICP, da es etwas fluktuierte, der 32u4 hat keine ISR Prios. Ein paar andere Feinheiten, erstmal in Forum wegspeichern, ich hoffe das ich am WE Zeit finde, um alles noch zu kommentieren und den MPP als solche reinzubasteln.
RC Anschluss nun D2+3+4 (ich löse INT0+1 aus, bei einer ICP-ISR müsste ich den Pin abfragen, da die drei Pins aber so hübsch nebeneinander liegen, kann man es auch nutzen). Regler nun abweichend D5, da ich Timer1 und 3 tauschen musste, um den ICP1 für RC zu nutzen.

RC wird nun eingelesen, als 500Hz PPM an den Regler weitergeben, auf dem Sender als RPM angezeigt, und auf dem Serialmonitor angezeigt (das Serial.print dazu aktivieren).


Code:
volatile uint16_t RC=0;
volatile uint8_t  HOTT[46] = {0x7C,0x8D,0x00,0xD0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                              0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                              0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7D,0x00};


void setup() {
    UBRR1L = 0b00011001;  // HOTT-UART 19200 Baud         
    UCSR1B = 0b10011000;  // Hott-RX Interrupt aktiviert
    TCCR4A = 0b00000000;  // Hott-Delay-Timer läuft alle 2048uS über
    TCCR4B = 0b00000110;  // Hott-Delay-Timer Prescale 1/32
    TIMSK4 = 0b00000100;  // Hott-Delay-Timer Overflow ISR aktiviert 
    
    PORTD |= 0b00010000;  // Read RC Pullup aktiviert
    TCCR1A = 0b00000000;  // Read RC Normal PWM-Mode 
    TCCR1B = 0b01000010;  // Read RC ICP aktivieren, Prescale 1/8
    EIMSK  = 0b00000011;  // Read RC Interupts INT0 + INT1 aktiviert
    EICRA  = 0b00001011;  // Read RC Interrupt INT1=falling-edge INT0=rising-edge

    DDRC  |= 0b01000000;
    TCCR3A = 0b10000010;  // PPM_OUT
    TCCR3B = 0b00011010;  // PPM_OUT
    ICR3   = 2000;        // PPM_OUT

    sei();
    Serial.begin(19200);  
}



void loop() {
    OCR3A = RC;                       //Test RC(Senderknüppel)als 500Hz PPM ausgeben
    uint16_t  RPM = RC;               //Test RC(Senderknüppel)für RPM übernehmen
    HOTT[21] = (uint8_t)(RPM&255);    //Test RC als RPM auf dem Sender ausgeben 
    HOTT[22] = (uint8_t)(RPM >>8);    //Test RC als RPM auf dem Sender ausgeben
  //Serial.println(RC);  delay(50);   //Test RC auf Serialmonitor ausgeben. 
}



// ------------- Read-RC-ISR-Routinen ----------------- //
ISR(INT0_vect){           // Read RC- rising edge
    OCR1A  = ICR1;        // Timestamp in OCR1A zwischenspeichern
    TCCR1B = 0b00000010;  // ICP auf falling edge stellen
} 
ISR(INT1_vect){           // Read RC- falling edge,
    RC     = ICR1-OCR1A;  // RC berrechnen
    TCCR1B = 0b01000010;  // ICP auf rising edge stellen
    TCNT1  = 0b00000000;  // Zähler zurücksetzen um Überläufe zu meiden    
} 



// ------------ HOTT-ISR-Routinen------------------------//
ISR(USART1_RX_vect){
if  (UDR1     == 0b10001101)      // Hott-GAM hat nach Daten gerufen, Senderoutine wird gestartet:
{   UCSR1B     = 0b00001000;      // Hott-RX-Interrupt wird während der Senderoutine gesperrt,
    HOTT[44]   = 0b00000000;      // Hott-Checksum auf Null stellen,
    HOTT[45]   = 0b11111110;      // Hott-Counter-Byte zurücksetzen 3 OVFs Vorlauf für 6mS
    TCNT4      = 0b10000000;      // Hott-Delay-Timer zurücksetzen 1/2 Cycle abkürzen für 5mS
    TCCR4B     = 0b00000110;      // Hott-Delay-Timer starten, 2048uS pro Überlauf
}}

ISR(TIMER4_OVF_vect){             // Hott-Delay-Timer läuft im Sendebetrieb alle 2048uS über
if  (HOTT[45]  < 0b00101101)      //  Solange noch nicht 45 alle Bytes gesendet sind, Sendung mit der Maus:
{                                         //while (!(UCSR1A & (1<<UDRE1))){} <---WIRKLICH NÖTIG ??? 
    UDR1       = HOTT[HOTT[45]];  //  aktuelles Hott-Byte schreiben
    HOTT[44]   = HOTT[44]         //  Hott-Checksum HOTT[44] bauen, es wird nur das L-Byte gesammelt,
               + HOTT[HOTT[45]];  //  und mit dem aktuellem Hott-Byte addiert.      
}else{  
if  (HOTT[45] == 0b00101101)      // Wenn alle 45 Bytes gesendet wurden, ist Sendeschluss: 
{   TCCR4B     = 0b00000000;      //  Hott-Delay-Timer stoppen, keine OVFs mehr.
    UCSR1B     = 0b10011000;}     //  Hott-RX-Interrupt wieder aktivieren (Empfangsbereit -ohne GEZ).            
}   HOTT[45] ++ ;                 //  Nach jedem Durchlauf wird das Hott-Counter-Byte um 1 erhöht. 
}
 
Ist mir grad noch beim Frühstück eingefallen, das übliche synchronisieren über den PPM-Timer Überlauf wie gehabt, einfach das Serial-print als Timestamp für die Loop nutzen. Wir haben 2mS Zeit für den MPP Code in der Loop, besser deutlich weniger, bei 1500uS spätestens Schluss machen. Im Serialmonitor sieht man auch schön die etwas höhere Zeit, sobald man den Sender dazuschaltet, und die Hott-ISRs dazwischenfunken. Es sind kaum 10uS. Der "Blockierfreie" Hott Code funzt also :)

die neue Loop:

Code:
void loop() {
if (TIFR3  & (1<<TOV3)){       // sync with PWM-Timer 3, alle 2mS, Loop aber sollte nennenswert kürzer sein
    TIFR3 |= (1<<TOV3) ;       // OVF1 Flag löschen

    OCR3A = RC;                       //Test RC(Senderknüppel)als 500Hz PPM ausgeben
    uint16_t  RPM = RC;               //Test RC(Senderknüppel)für RPM übernehmen
    HOTT[21] = (uint8_t)(RPM&255);    //Test RC als RPM auf dem Sender ausgeben 
    HOTT[22] = (uint8_t)(RPM >>8);    //Test RC als RPM auf dem Sender ausgeben

    delay(1); //testweise 1000uS verplempern... 
Serial.println(TCNT3);   //Zwischenzeit der Loop in uS ausgeben 
}}
 
Update Alpha0-Version

Update Alpha0-Version

Heute scheint die Sonne, also habe ich mal fix einen 0815 MPPC mit drauf gespielt.
im Display :
erste Reihe; 63,0V entspricht 6,3V real
zweite Reihe: 63,0A entspricht 6,3V MPP ,
dritte Reihe: 17000rpm entspricht 1700uS Regler PPM
unten links: ausgehende PPM in %
unten rechts; MCU Temperatur vom Arduino.
Das kann man noch irgendwann mal schicker anzeigen, so war es erst mal alles auf einen Blick.

Die Regelung rennt noch etwas stockelig, aber es funzt soweit, man könnte damit sogar problemlos fliegen.



Als nächstes aber erstmal den Code aufräumen, (und blöde Kommentare drannbibben:cry:).
Ich stell die "Alpha-minus3 Version trotzdem mal ein Datensicherung ;) (falls das hier überhaupt jemand interessiert)

Code:
// MPPC testversion für Solarmodelle !! Beschreibung und Fragen im RC-N Forum.
// In der IDE bitte "Lilipad Arduino USB" benutzen. 
// Der Code funktioniert ausschließlich auf einem 8MHz 32u4-Board ! 
// Regler -- D5  --------> gibt das Reglersignal in 500Hz aus. 
// RC-Kanal -- D2 + D3 + D4 ---> liest den Gaskanal des Senders ein
// Hott-Telemetriebuchs -- D0(Rx) + über einen 1,2K Widerstand nach D1(Tx)
// Solarspannung A1 über Spannungsteiler Aref = 2,56V

#define Telemetrie_HOTT        // Telemetrie_HOTT Telemetrie_OFF Telemetrie_JETI   ......   

volatile uint16_t RC=0, U_SOL, MPP, Temp;

void setup() {               Serial.begin(500000);
    PORTD |= 0b00010000;  // Read RC Pullup aktiviert
    TCCR1A = 0b00000000;  // Read RC Normal PWM-Mode 
    TCCR1B = 0b01000010;  // Read RC ICP aktivieren, Prescale 1/8
    EIMSK  = 0b00000011;  // Read RC Interupts INT0 + INT1 aktiviert
    EICRA  = 0b00001011;  // Read RC Interrupt INT1=falling-edge INT0=rising-edge

    DDRC  |= 0b01000000;  // PPM_OUT
    TCCR3A = 0b10000010;  // PPM_OUT
    TCCR3B = 0b00011010;  // PPM_OUT
    ICR3   = 2000;        // PPM_OUT}    

    TELEMETRIE_init();
    OCR3A = 1100; //Regler initialisieren 
    delay(2500);  //warten bis Regler initialisiert ist 
}

void loop() {
while (TIFR3  & (1<<TOV3)){       // sync with PWM-Timer 3, alle 2mS, Loop aber sollte nennenswert kürzer sein
       TIFR3 |= (1<<TOV3) ;       // OVF1 Flag löschen      

       MPP =  RC/2.1;        // Professor-ium für 12Zellen 2,1
       TELEMETRIE_send();                   
       analog_Read();       //liest Solarspannung und Temp-Sensor

  if (MPP > U_SOL)  {if (OCR3A > 1100 ) {OCR3A--;}} // MPPC Voltage to Low
  else              {if (OCR3A < 1900 ) {OCR3A++;}}   // MPPC Voltage to High 

Serial.println(TCNT3);   //debug (TCNT3 gibt die verbrauchte Zeit in mS der Loop aus)
}}



// ----------------------- analog_Read setup START ----------------- //
void  analog_Read(){
      ADMUX  = 0b11000111; //ADC Tempsensor
      ADCSRB = 0b00100000;
      ADCSRA = 0b11000110;     
      while (ADCSRA & (1<<ADSC)) ;
      Temp = ADCW-277;         //KALIBRIEREN !!!!
      
      ADMUX  = 0b11000110; //ADC6
      ADCSRB = 0b00000000;
      uint16_t Utemp = 0; 
for  (int i=1; i <= 5; i++){    
      ADCSRA |= (1<<ADSC);  
      while (ADCSRA & (1<<ADSC)) ;
      Utemp = Utemp + ADC; 
}     U_SOL = Utemp / 7.13;       // auf centiVolt realwert kalbrieren mit 7,13
}// ----------------------- analog_Read  setup ENDE ----------------- //


// ----------------------- Read-RC-ISR-Routinen ----------------- //
ISR(INT0_vect){           // Read RC- rising edge
    OCR1A  = ICR1;        // Timestamp in OCR1A zwischenspeichern
    TCCR1B = 0b00000010;  // ICP auf falling edge stellen
} 
ISR(INT1_vect){           // Read RC- falling edge,
    RC     = ICR1-OCR1A;  // RC berrechnen
    TCCR1B = 0b01000010;  // ICP auf rising edge stellen
    TCNT1  = 0b00000000;  // Zähler zurücksetzen um Überläufe zu meiden    
}// ----------------------- RPM_IN_OUT setup ENDE ----------------- //


// ------------------------HOTT TELEMETRIE BEGIN ------------------//
#ifdef Telemetrie_HOTT 
 
volatile uint8_t  HOTT[46];
   
void TELEMETRIE_send(){     //https://github.com/Ziege-One/Strom2Hott/blob/master/Message.h
    HOTT[0]   = 0x7C;
    HOTT[1]   = 0x8C; 
    HOTT[3]   = 0xC0;
    HOTT[6]   = (uint8_t)(U_SOL&255);     //#06 voltage 0.1V steps. 55 = 5.5V
    HOTT[7]   = (uint8_t)(U_SOL>>8);      //#07 MSB
    HOTT[8]   = (uint8_t)(MPP&255);       //#08 voltage min 0.1V steps. 55 = 5.5V     
    HOTT[9]   = (uint8_t)(MPP >>8);       //#09 MSB    
  //HOTT[10]  = (uint8_t)(mAh);           //#10 used battery capacity in 10mAh steps
  //HOTT[11]  = (uint8_t)(mAh);           //#11 MSB                                          
    HOTT[12]  = Temp+20;                  //#12 Temperature 1. Offset of 20. a value of 20 = 0°C)  
  //HOTT[13]  = Temp1B;                   //#13 Temperature 1. max Offset of 20. a value of 20 = 0°C
    HOTT[14]  = (uint8_t)(MPP&255);       //#14 current in 0.1A steps 100 == 10,0A 
    HOTT[15]  = (uint8_t)(MPP >>8);       //#15 MSB
  //HOTT[16]  = (uint8_t)(AmpMax);        //#14 current in 0.1A steps 100 == 10,0A
  //HOTT[17]  = (uint8_t)(Amp_Max);       //#15 MSB
    HOTT[18]  = (uint8_t)(OCR3AL);        //#18 RPM in 10 RPM steps. 300 = 3000rpm   
    HOTT[19]  = (uint8_t)(OCR3AH);        //#19 MSB
    HOTT[20]  = (uint8_t)(0x6c);          //#18 RPM max in 10 RPM steps. 300 = 3000rpm   
    HOTT[21]  = (uint8_t)(0x07);          //#19 MSB    
    HOTT[22]  = OCR3A/8-117;              //#22 Temperature 2. Offset of 20. a value of 20 = 0°C
  //HOTT[23]  = Temp2A;                   //#23 Temperature 2. max Offset of 20. a value of 20 = 0°C  
    HOTT[43]  = 0x7D;
}

void TELEMETRIE_init(){
    UBRR1L    = 0b00011001;     // Hott-UART 19200 Baud         
    UCSR1B    = 0b10011000;     // Hott-RX Interrupt aktiviert
    TCCR4A    = 0b00000000;     // Hott-Delay-Timer läuft alle 2048uS über, wird erst in der Hott-ISR gestartet
    TCCR4B    = 0b00000000;     // Hott-Delay-Timer STOP, damit ohne Hott steht, aktiviert sich in der Hott-ISR
    TIMSK4    = 0b00000100;     // Hott-Delay-Timer Overflow ISR aktiviert 
}

ISR(USART1_RX_vect){
if  (UDR1    == HOTT[1])         // Hott hat nach Daten gerufen, Senderoutine wird gestartet:
{   UCSR1B    = 0b00001000;      // Hott-RX-Interrupt wird während der Senderoutine gesperrt,
    HOTT[44]  = 0b00000000;      // Hott-Checksum auf Null stellen,
    HOTT[45]  = 0b11111110;      // Hott-Counter-Byte zurücksetzen 3 OVFs Vorlauf für 6mS
    TCNT4     = 0b10000000;      // Hott-Delay-Timer zurücksetzen 1/2 Cycle abkürzen für 5mS
    TCCR4B    = 0b00000110;      // Hott-Delay-Timer starten, 2048uS pro Überlauf, Prescale 1/8
}}

ISR(TIMER4_OVF_vect){             // Hott-Delay-Timer läuft im Sendebetrieb alle 2048uS über
if  (HOTT[45] < 0b00101101)       //  Solange noch nicht 45 alle Bytes gesendet sind, Sendung mit der Maus:
{                                         //while (!(UCSR1A & (1<<UDRE1))){} <---WIRKLICH NÖTIG ??? 
    UDR1      = HOTT[HOTT[45]];   //  aktuelles Hott-Byte schreiben
    HOTT[44]  = HOTT[44]          //  Hott-Checksum HOTT[44] bauen, es wird nur das L-Byte gesammelt,
              + HOTT[HOTT[45]];   //  und mit dem aktuellem Hott-Byte addiert.      
}else{  
if  (HOTT[45]== 0b00101101)       // Wenn alle 45 Bytes gesendet wurden, ist Sendeschluss: 
{   TCCR4B    = 0b00000000;       //  Hott-Delay-Timer stoppen, keine OVFs mehr.
    UCSR1B    = 0b10011000;}      //  Hott-RX-Interrupt wieder aktivieren (Empfangsbereit -ohne GEZ).            
}   HOTT[45] ++ ;                 //  Nach jedem Durchlauf wird das Hott-Counter-Byte um 1 erhöht. 
}
#endif // ------------------------HOTT TELEMETRIE ENDE ----------------------------------------//


#ifdef Telemetrie_JETI //Platzhalter für JETI Telemetrie
void TELEMETRIE_send(){}
void TELEMETRIE_init(){}
#endif

#ifdef Telemetrie_OFF  //Platzhalter für mit-ohne Telemetrie
void TELEMETRIE_send(){}
void TELEMETRIE_init(){}
#endif
 

derjuwi

User
Hi!

ehrlichgesagt ist das keine sehr gute regelperformance. Vermutlich zum Teil dem Hott Telemetriekram geschuldet. Deine Spannung ist instabil selbst auf deinem langsamen Hott Feedback ablesbar schwankend, du hast einbrueche bis auf 5V und Ueberschwinger ohne Ende...

Ich biete dir hiermit nochmal an deine Regelungsstechnik zu analysieren und dir zu helfen. Das ist so wie es ist nicht gut.

Du willst ohne RC Akku fliegen... So wird das nix.

Gruesse Julian
 
Moin Julian

Danke
guggsd du hier "quote': Heute scheint die Sonne, also habe ich mal fix einen 0815 MPPC mit drauf gespielt. "Die Regelung rennt noch etwas stockelig," >>>> ich würde es sogar grotttig nennen, ging ja nur um fix die Grundfunktion mal auszutesten. Mit der Reglung geht es ja jetzt erst los. In Kooperation mit Telemetrie ist für mich Neuland, aber interessant :)

Dadurch das die Regelschleife nur noch mit 500Hz rennt, ist das so mit nur PWM ++ und -- natürlich viel zu langsam, das schwingt über, weil es schlicht zu träge ist.

Bei meiner aktuellen Steuerung bin ich in der loop ja mit den 4KHz OneShot synchronisiert, da ist das kein Problem, Sind die Abweichungen groß, regelt er in jedem Durchlauf, werden sie kleiner, dann nur jedes zweite Mal, noch kleiner, dann jedes vierte Mal, u.s.w. bis 16. Das funzt super, die Spannung steht wie angenagelt, und in der Ausgangs-PPM pumpt und schwingt es nicht :)
Zeit für Telemetrie ist so da aber nicht mehr, darum wieder 500Hz.


Hatte vorhin auch mal fix nen professorisches P dazugebastelt, (wenig Zeit....) selbst das ist schon ein Unterschied wie Tag und Nacht, bleibt so vermutlich aber auch nicht -;) .

Code:
//P= je grösser die Abweichung je grösser die Regelschritte. Je kleiner der Wert, desto größer die Schritte
 uint8_t P = 10; 
 if (U_SOL > MPP)   {if (OCR3A < 1900 ) {OCR3A += +1 + ((U_SOL - MPP) / P); }}  
 else               {if (OCR3A > 1100 ) {OCR3A += -1 - ((MPP - U_SOL) / P); }}


So ungefähr wollte ich das mal ausprobieren (PI-mitohneD) , Spielecode, Ausgabe in 100er Slow-Motion -I schwingt noch hinterher....
Code:
int P = 2;        //P-Anteil -Userwert
int I = 2;        //I-Anteil -Userwert

uint16_t VOLT=  610;  //U-IST dummywert für Spannung

uint16_t  MPP=  630;  //U-SOLL

uint16_t PWM=1500;
int PID_i[10];        //ARRAY für I-Anteil
int z;                //Zähler

void setup() {Serial.begin(19200);}
void loop() {   

PID_i[z] = VOLT-MPP;     // IST minus SOLL Regelung verdreht als PID-üblich, da durch abfallende PWM die Spannung steigt.

int temp = 0;   for(int i = 0; i <10 ; i++)    {temp += PID_i[i];}    temp = temp / 10;

int PID_I = temp / I;
int PID_P = PID_i[z] / P;

z++; if(z>10){z=0;}

PWM += +PID_I +PID_P;

if(PWM>1900){PWM=1900;}
if(PWM<1100){PWM=1100;}

Serial.print("  P:");Serial.print(PID_P);
Serial.print("  I:");Serial.print(PID_I);
Serial.print("  PWM:");Serial.println(PWM);delay(200);
}
 

maccl

User
Hi Holger

sieht doch gar nicht so schlecht aus.:cool:

aber nochmal zur Hardware.

Du hast das jetzt für den 32u4 bzw 328p geschrieben richtig?

Hab mir in der Zwischenzeit mal einen USB-Tiny-ISB mit Attiny44 und Attiny85 besorgt, um auch mal deine Vorgängerversionen und den Ukranduit zu testen. Aber es macht vermutlich keinen Sinn oder?

Ist nur etwas schwierig in dem "Solarflieger"-Fred die Code-Schnipsel zu finden.

Wäre dir wirklich sehr sehr sehr dankbar wenn du das mal bei Github reinstellen könntest.
und evtl alle Versionen mit allen Board-Optionen

DANKE

Gruß Maccl
 

maccl

User
nettes Feature:
die PID - Werte könnte man in Zukunft via Textmode ändern, dann könnt man am Platz einfach Anpassungen vornehmen.
 

derjuwi

User
Eine Solarzelle ist kein System das man per PI oder PID regeln kann, resp. muss. Eine Solarzelle hat keine Masse die beschleunigt werden muss und keine Traegheit in ihrer Reaktion.
Auserdem ist sie nichtstetig in ihrem Verhalten, eine erhoehung der PWM kann einen Anstieg und einen Abfall der Leistung bewirken.

Am besten hat bei mir immer ein schwingender Regler mit geringer Hysterese funktioniert. Bei nur 500Hz Stellfrequenz muss man aber hier dolle aufpassen um nicht wie oben Holle in seinem Beispiel instabil zu werden.

Die Regelfrequenz mit der ein Regler laeuft darf nicht hoeher sein als die Haelfte der Zeit die der Regler braucht um die Aenderung zu sehen die er gestellt hat.


Ist ja auch logisch, sonst sieht man ja noch garnicht den Erfolg der Massnahme.

Ein neuer Durchlauf der Regelschleife darf erst erfolgen wenn der Ausgangswert ueber den Motorsteller zur PWM erhoehung und damit zur Veraenderung der Solarspannung gefuehrt hat. Die dafuer erforderliche Zeit ist mindestens zu verdoppeln um sauber abtasten zu koennen.

Bei 500Hz und einer schnellen uebernahme des RC Wertes in echte MotorPWM wuerde ich mal aus dem Bauch schaetzen das nach 10ms ca. ein neuer Spannungswert messbar sein sollte. Die Regelschleife sollte also max. mit 50Hz(2*10ms) laufen... Das ist keine pessimistische Schaetzung, eher optimistisch.

Gruesse Julian
 
Die Regelfrequenz mit der ein Regler laeuft darf nicht hoeher sein als die Haelfte der Zeit die der Regler braucht um die Aenderung zu sehen die er gestellt hat.

Hallo Julian,

die Abtastzeit TA bei einem digitalen Regler muss kleiner als die bestimmende Systemzeitkonstante Ts der Regelstrecke. Ansonsten bekommt der Regler änderungen im Istwert nicht schnell genug mit. Die Abtastfrequenz (Reglerfrequenz) muss also höher sein als...

Gruß
Micha
 
Hallo Micha
Genauso haben es mir unsere Informatik Kollegen im Verein auch erklärt, irgendwas mit im Fernsehen flimmernden karierten Sakos und Aliasing u.s.w.

Ich taste jetzt synchronisiert ab, einmal Messen, einmal ausgeben, taktgleich, dann sollte es eigentlich passen.

Hallo maccl
Hott-Texmodus ist sooo gemein, und sooo träge... Ich red mich mal damit raus, das funzt nur mit Hott ;)



Hallo Julian

Randbedingungen:
Reglerauflösung 800Schritte
Übertragungsfrequenzen zum Regler typisch 500Hz oder 4000Hz
Moderne Regler können natürlich beides noch mehr, aber es soll ja universell sein,

Praktischer Erfahrungswert für "lineares" Regeln (Plus Minus schrittgleich) Leerlauf - Vollgas ca. 0,5Sekunden ist eine gute Ausgangsbasis.

Mal überschlagen:
Ergibt eine Regelschleife von 800steps Auflösung / 0,5Sekunden = 1,6KHz
bei 500Hz und 800er Auflösung sind wir schon bei ca 1,5Sekunden, das ist natürlich viel zu träge.
Erhöht man die Schrittgröße, würde man die Auflösung verringern.

Also entweder hochfrequent regeln, oder zumindest ein quasi-P mit rein, beides hat sich in der Praxis mittlerweile bewährt. Mit dem P-light in der synchronisierten Schleife bin ich ja die 2018er Saison geflogen.
Mit zusätzlichem I habe ich noch nicht im Modell experimentiert, das folgt genau ab jetzt
smile.gif


Die 4KHz nutze ich nicht um 4000x pro Sekunde zu übertragen, sondern weil ein neuer Wert nur 0,25mS zur Übertragung benötigt, statt wie herkömmlich 2ms. Der Regler hat dadurch satte 1,75mS mehr Zeit (7mal so viel) den neuen Wert umzusetzen, sobald ein neuer Wert feststeht.




Einen Einfluss der Telemetrie auf die Regelung kann ich zu 99,999% ausschließen, die ISRs können maximal einmal pro Regelschleife auftauchen, nehmen mir maximal 15uS weg. Die Regelschleife wird vom PWM-Timer per Overflow-Flag alle 2mS gestartete, läuft so synchron zur Ausgabe. Der Code darin benötigt derzeit knapp unter 1mS (unten mit dem Serial.print messbar) , da tun die 15uS nicht weh. (pure Absicht, deswegen habe ich die Telemetrie ja kmpl. neu geschrieben
biggrin.gif
).

Ich habe vorhin mal ein kurzes Video mit der Änderung vom vorigem Beitrag gemacht, P=5 (entspricht P=0,2 typischer PID). Dafür das sämtliches Feintuning noch aussteht (zu kalt mit Notebook da draußen
wink.gif
), und alles so über den Daumen gepeilt aus dem Ärmel geschüttelt ist, rennt es jetzt schon recht ordentlich.

Die einzige Änderung zwischen den beiden Videos:
Vorher:
Code:
  if (MPP > U_SOL)  {if (OCR3A > 1100 ) {OCR3A--;}} // MPPC Voltage to Low
  else              {if (OCR3A < 1900 ) {OCR3A++;}}   // MPPC Voltage to High

Nachher ( P= jeh grösser die Abweichung je grösser die Regelschritte. Je kleiner der Wert, desto größer die Schritte)
Code:
 uint8_t P = 5; 
 if (U_SOL > MPP) {if (OCR3A < 1900) {OCR3A += +1 + ((U_SOL - MPP) / P); }} //Solarspannung zu hoch, erhöhe PWM
 else             {if (OCR3A > 1100) {OCR3A += -1 - ((MPP - U_SOL) / P); }} //Solarspannung zu gering, veringere PWM


 

derjuwi

User
Hallo Julian,

die Abtastzeit TA bei einem digitalen Regler muss kleiner als die bestimmende Systemzeitkonstante Ts der Regelstrecke. Ansonsten bekommt der Regler änderungen im Istwert nicht schnell genug mit. Die Abtastfrequenz (Reglerfrequenz) muss also höher sein als...

Gruß
Micha
Leuchtet mir nicht ein...
Weil ja, ich verstehe was du meinst, aber was fuer einen Sinn macht es den Regelkreis zweimal zu durchlaufen in der Zeit in der ich noch nicht einmal dem Stellglied etwas sagen konnte?
Das ich den ADC Wert integrieren muss ueber die selbe Zeit ist klar.

Da kann ich aber einfach einen IIR Filter anwenden.

Gruesse Julian
 
Hallo Julian
Dann müsste ich mir das Tektronix ausleihen. Erstmal aber fertigmachen, Spannung und PPM-OUT habe ich ja auch im 500Hz Log, das allermeiste sieht man da schon bereits, die ganz kurzen Schlenker sieht man da natürlich nicht.
Ich trigger sonst immer das alte Hameg auf die 500Hz PWM, dann habe ich ein (flüchtiges) 2mS Fenster in der Glotze.:rolleyes:


@all

Wie wäre es mit einem klassischem SerialTerminal Menü ? SerialTerminal ist einfach und Plattformaunabhängig, funzt auch mit dem Smartphon und OTG-USB Adapter.

Hab mal was hingetippt (Arduinio----*duck*) sollte auf jedem USB-Arduino funzen, vielleicht mag mal wer mit spielen, und Resonanz geben.

Code:
uint8_t A=12,B=23,C=34,D=45,E=56,F=67,G=78;

void setup() {
Serial.begin(19200);
helpscreen();
listscreen();
}

void loop() {}

void serialEvent() {  
if (Serial.available()){
uint8_t inByte=Serial.read(); 
if(inByte=='A') {A = Serial.parseInt();  Serial.print("A change in: "); Serial.println(A);}  
if(inByte=='B') {B = Serial.parseInt();  Serial.print("B change in: "); Serial.println(B);} 
if(inByte=='C') {C = Serial.parseInt();  Serial.print("C change in: "); Serial.println(C);} 
if(inByte=='D') {D = Serial.parseInt();  Serial.print("D change in: "); Serial.println(D);} 
if(inByte=='E') {E = Serial.parseInt();  Serial.print("E change in: "); Serial.println(E);} 
if(inByte=='F') {F = Serial.parseInt();  Serial.print("F change in: "); Serial.println(F);} 
if(inByte=='G') {G = Serial.parseInt();  Serial.print("G change in: "); Serial.println(G);} 
if(inByte=='H') {helpscreen();} 
if(inByte=='L') {listscreen();}
}}

void helpscreen(){ 
Serial.println();
Serial.println("MPPC for Solarmodels");
Serial.println("type H for help");
Serial.println("type L for list Parameter");
Serial.println("to change a parameter, type A123 for change A with value 123");
Serial.println("Bitte unten Zeilenumbruch im Serialmonitor wählen");
Serial.println();

}  
void listscreen(){
Serial.println( );           
Serial.print("Parameter A: ");Serial.println(A);
Serial.print("Parameter B: ");Serial.println(B);
Serial.print("Parameter C: ");Serial.println(C);
Serial.print("Parameter D: ");Serial.println(D);
Serial.print("Parameter E: ");Serial.println(E);
Serial.print("Parameter F: ");Serial.println(F);
Serial.print("Parameter G: ");Serial.println(G);
Serial.println();
}

So sieht das dann aus (irgendwie 80er :)) -Wichtig ist unten den Zeilenumbruch zu wählen -(schreib ich gleich mit in den helpscreen)
Term.jpg
 
Yeahh, C64 lässt grüssen :D:cool:
Code:
  _ __ ___  _ __  _ __   ___  
 | `_ ` _ )| '_ )| '_ ) / __) 
 | | | | | | |_) | |_) | (__  
 |_| |_| |/| .__/| .__/ (___) 
           |_|   |_|     for Solarmodelling

 >>> type H for help, D for Doku
 Telemetrie 0=OFF 1=HOTT 2=JETI   (0) T = 0
 Tempsensor 0=OFF,1=INT 2=LM35?   (1) C = 2
 Tempsensor calibration         (127) S = 66
 Voltage calibration:           (127) V = 123
 MPP-comp 0=OFF 10=STRONG         (5) M = 3
 P-GAIN PID 0=OFF 10=FAST         (5) P = 6
 I-GAIN PID 0=OFF 10=FAST         (5) I = 5
 
Ich würde das mal so abliefern, mag mal Jemand nach Denkfehler drüberschauen, Bevor ich das mit dem MPPC Code verheirate ?
Sollte auf jedem USB-Arduino laufen.
Code:
#include <EEPROM.h>
 uint32_t U_SOL=635, PWM=1567, MPP=630 , Looptime=1200; //dummywerte

void setup() {
  Serial.begin(19200);
  logo();
  helpscreen();
}
void loop() {}

// ++++++++++++++++++++++++++++++++ SERIAL MENUE BEGIN ++++++++++++++++++++++++++++++++++++++++++++++ //
uint8_t T,C,S,V,M,P,I;

void serialEvent() {
  if (Serial.available()) { uint8_t inByte = Serial.read();
  if(inByte== '#') {Serial.println(); Serial.println(U_SOL); Serial.println(MPP); Serial.println(PWM);}else{
  if(inByte== 'x') {Serial.println(Looptime);}else{  //secret function, show Timestamp LOOP, max 1500uS (2000)
  if(inByte== 'H') {helpscreen();} 
  if(inByte== 'D') {DOKU();}  
  if(inByte== 'T') {T = Serial.parseInt(); EEPROM.update(0x01, T);helpscreen();}
  if(inByte== 'C') {C = Serial.parseInt(); EEPROM.update(0x02, C);helpscreen();}
  if(inByte== 'S') {S = Serial.parseInt(); EEPROM.update(0x03, S);helpscreen();}
  if(inByte== 'V') {V = Serial.parseInt(); EEPROM.update(0x04, V);helpscreen();}
  if(inByte== 'M') {M = Serial.parseInt(); EEPROM.update(0x05, M);helpscreen();}
  if(inByte== 'P') {P = Serial.parseInt(); EEPROM.update(0x06, P);helpscreen();}
  if(inByte== 'I') {I = Serial.parseInt(); EEPROM.update(0x07, I);helpscreen();}
}}}}
void helpscreen() {
  Serial.print("\n >>> type H for help, D for Doku\n");
  T = EEPROM.read(0x01);  Serial.print(" Telemetrie 0=OFF 1=HOTT 2=JETI   (0) T = "); Serial.println(T);
  C = EEPROM.read(0x02);  Serial.print(" Tempsensor 0=OFF,1=INT 2=LM35?   (1) C = "); Serial.println(C);
  S = EEPROM.read(0x03);  Serial.print(" Tempsensor calibration         (127) S = "); Serial.println(S);
  V = EEPROM.read(0x04);  Serial.print(" Voltage calibration:           (127) V = "); Serial.println(V);
  M = EEPROM.read(0x05);  Serial.print(" MPP-comp 0=OFF 10=STRONG         (5) M = "); Serial.println(M);
  P = EEPROM.read(0x06);  Serial.print(" P-GAIN PID 0=OFF 10=FAST         (5) P = "); Serial.println(P);
  I = EEPROM.read(0x07);  Serial.print(" I-GAIN PID 0=OFF 10=FAST         (5) I = "); Serial.println(I);
}
void logo(){  
  Serial.println("  _ __ ___  _ __  _ __   ___  ");
  Serial.println(" | `_ ` _ )| '_ )| '_ ) / __) ");
  Serial.println(" | | | | | | |_) | |_) | (__  ");
  Serial.println(" |_| |_| |/| .__/| .__/ (___) ");
  Serial.println("           |_|   |_|     for Solarmodelling");
}
void DOKU(){ logo();
  Serial.println("\n >>> type H for help, D for Doku\n");  
  Serial.println(" type # for show live Data (Voltage MPP PPM)");
  Serial.println(" type X123 to change parameter X to value 123");
  Serial.println(" Blah-Blah-Doku-Blah-Blah");
}
// ++++++++++++++++++++++++++++++++ SERIAL MENUE END ++++++++++++++++++++++++++++++++++++++++++++++ //
 

maccl

User
Hott-Texmodus ist sooo gemein, und sooo träge... Ich red mich mal damit raus, das funzt nur mit Hott ;)

Hi Holger,
warum träge? Die Einstellungen macht man eh am Boden ;)

kann da auch gerne beim Textmode helfen, wär aber halt gut wenn du ein Github-Projekt machst.




zu deinem SerialTerminal:
ich finde es nicht schlecht (auch das Logo)
aber schau dir doch mal an wie das die betaflight-Jungs machen.
das könnte man bestimmt übernehmen (oder Teile davon)

https://github.com/betaflight/betaflight/wiki/Betaflight-specific-CLI-commands
https://github.com/betaflight/betaf...8ebc0a40af38990941fd/src/main/interface/cli.c


oder was auch noch eine gute Möglichkeit wäre, wenn man auf bestehendes Protokoll zurückgreift
zb MWP oder JSON
Dafür gabe es dann opensource GUIs die man dann so anpassen kann wie man es braucht
http://www.multiwii.com/wiki/index.php?title=MultiWii_GUI

würde aber eher Json nehmen zb hier
https://github.com/bblanchon/ArduinoJson




kannst du mir sagen wo ich deine 85er-Software mit Knüppel-Modus finde?



Danke

Gruß Maccl
 
Moin Maccl

http://www.rc-network.de/forum/showthread.php/505097-Solarflieger?p=4666023&viewfull=1#post4666023 Testversion von vor kurzem
https://www.rcgroups.com/forums/showpost.php?p=38818609&postcount=120 Testversion von vor einem Jahr -pingleich mit Ukranduid
Ich bin selber nie mit dem 85er geflogen

Gihub und json habe ich probiert, ist mir alle viel zu englisch, komm ich schlicht nicht mit klar, und würde dafür vermutlich Stunden versenken. :(

Der 32u4 ist ein kleiner gemeiner Giftzwerg, da lief das Serialmenü gemütlich auf dem Nano, aber die 32u4 Zicke weigerte sich. Nach kurzem googlen, der kann kein Interrupt auf dem USB :eek: Es ist auch völlig egal was ich in Serial.begin(123) schreibe, haupsache irgend ne Zahl, und auch welche Baudrate man im Serialmonitor wählt :D AVR+USB ist wie ein Fahrradlenker im Auto ;)

Sei es drumm. Mein Gedanke ist, das zumindest die Harwarebezogenen Parameter auf dem Eeprom landen, damit sie nach einem Update noch da sind. Hab das jetzt ganz minimalistisch gelöst, nur wenn der Motor auf Aus ist, fragt der nach dem Serialport.

Soll ich mal eine Zwischenversion einstellen ?


btw; der neue MPPC mal um Formel-iert. Muss den aber nochmal durch den Wolf drehen, scheint aber soweit zu passen.
Die oberen drei Parameter sind zum testen
Code:
void setup() {
Serial.begin(19200);
uint8_t  Temp_20 = 30;  // 0=-20°C / 20=0°C / 60=40°C 
uint16_t RC =  1800;    // RC IN 1100-1900uS
uint16_t PWM = 1000;    // ausgehende PWM 1100-1900uS

uint32_t MPP  = 2400-RC;                       Serial.println(MPP);  //
         MPP -= MPP*Temp_20*3/1000;            Serial.println(MPP);  // Temperaturkompensation  
         MPP  = MPP*(1000-(2000-PWM)/20)/1000; Serial.println(MPP);  // Leistungskompensation
}
void loop() {}
 

maccl

User
Hi Holger

danke für die FW.


also JSON ist im Grunde nix dramatisches.
arduinoJSON ist eine Library für Arduino die es einem leichter macht die DatenPakete zu erstellen und zu parsen.
hier gibts ne deutsche WIKI https://de.wikipedia.org/wiki/JavaScript_Object_Notation

hier gibts einen Assi der zeigt wie das parsen geht
https://arduinojson.org/v5/assistant/

in dem man halt zb das ins Terminal schreibt könnte man einen Wert setzen ....
{ "parameter": "wert123" }

ein GUI dann später dafür zu machen wäre mit processing oder anderen Tools möglich

JSON ist halt sehr verbreitet, u.a HausSensorik usw....und eigentlich eine gute sache um datenpakete auzutauschen


ein einfaches tutorial
https://www.youtube.com/watch?v=z_rPvQhKTfY&frags=pl,wn



hier gäbe es ein git-Übersetzung ;)
https://github.com/danielauener/git-auf-deutsch
https://de.wikipedia.org/wiki/GitHub

aber Git bzw Github war für mich auch lange extrem verwirrend.
will es aber nicht mehr missen und empfehle dir dich damit mal auseinander zusetzen.


du kannst gern mal was einstellen, habe halt die Befürchtung das es hier untergeht.
 
vorläufiger Anschlussplan für die Beta1 Version.

Zum Einsatz kommt ein Arduino-Micro, wichtig ist das er einen 32u4 Prozessor mit 8MHz und 3,3Volt hat.
Bitte beim Kauf darauf achten, die hier vorgestellte Software funktioniert ausschließlich auf diesem Arduinoboard



Anschlussplan Oberseite

GND zu A1 Widerstand für den Spannungsteiler Solarspannung 10K (auf den Bild oben)
RX zu TX 1K2 für Hott (auf dem Bild unten links SMD, es darf auch ein bedrahteter Widerstand sein)
D2-D3-D4 mit einem Draht brücken.

MPPCTH2.jpg



Anschlussplan Unterseite
A1 zu Solarspannung mit 27K Widerstand für den Spannungsteiler in der Leitung. (auf dem Bild unten mitte rot)
RAW 5V BEC (auf dem Bild unten links 2x Rot)
RX zu T-Pin Hott (auf dem Bild oben links orange)
GND zu Minus Empfänger (auf dem Bild 2x oben links braun)
D4 zu Empfänger Gaskanal (auf dem Bild oben Mitte orange)
D5 zu Regler (auf dem Bild oben Mitte orange)

MPPCTH3.jpg
 
Ansicht hell / dunkel umschalten
Oben Unten