CUL disconnected und Wiederverbinden, Lösungsvorschlag

Begonnen von noansi, 12 April 2015, 15:46:57

Vorheriges Thema - Nächstes Thema

noansi

Hallo Rudolf,

ich hatte bisher an meinem RasPi das Problem, dass bei einem Watchdog ausgelösten CUL Reset CUL zwar auf diconnected ging, dann aber nur durch elektrisches Trennen und wieder Verbinden oder durch Neustart des RasPi zum reconnect zu bewegen war.
Dagegen ging es immer, mit raw B01 in den Bootloader zu kommen.

Daher habe ich nun folgende Änderungen bei meinem Entwicklungsprojekt vorgenommen:

in fncollection.c:

//////////////////////////////////////////////////
// boot
void
prepare_boot(char *in)
{
#if defined(HAS_WATCHDOG_TEST) || defined(HAS_PREPARE_BOOTLOADER)
  uint8_t bl = 0;
  if(in)
    fromhex(in+1, &bl, 1);

# ifdef HAS_WATCHDOG_TEST
  if(bl == 0xff) {           // test watchdog function
    while(1);
  }
# endif //HAS_WATCHDOG_TEST

# ifdef HAS_PREPARE_BOOTLOADER
  if(bl == 1) {              // Next reboot we'd like to jump to the bootloader.
    ewb( EE_REQBL, 1 );      // Simply jumping to the bootloader from here
                             // wont't work. Neither helps to shutdown USB
  }                          // first.
# endif // HAS_PREPARE_BOOTLOADER
#endif

#ifdef HAS_USB
  USB_ShutDown();            // Disconnect from USB bus
#endif
#ifdef HAS_FS
  fs_sync(&fs);              // Sync the filesystem
#endif


  TIMSK0 = 0;                // Disable the clock which resets the watchdog
  cli();
 
#ifdef HAS_USB
  wdt_enable(WDTO_4S);       // Make sure the watchdog is running, give USB host sufficient time to recognize the USB disconnect
#else
  wdt_enable(WDTO_2S);       // Make sure the watchdog is running
#endif
  wdt_reset();               // wait defined time before reset, so host hopefully recognizes dead USB/serial device
  while (1);                 // go to bed, the wathchdog will take us to reset
}



Die längere Watchdog Zeit gibt dem Host mehr Gelegenheit den Disconnect zu erkennen. #define HAS_WATCHDOG_TEST erlaubt mit raw BFF den Watchdog zu testen.
Damit es nun auch mit dem Watchdog Reset besser klappt (der alleine macht leider keinen USB_ShutDown() ), habe ich in CUL.c in main()

  wdt_enable(WDTO_2S);

geändert in

  wdt_enable(WDTO_8S); // 8 seconds for cc1101 TX switch with CCA enabled

und

  USB_Init();

ersetzt durch:

  // solve USB disconnect/reconnect after watchdog reset
  USB_Init();                         // first USB Init to have USB_ShutDown having an effect
  my_delay_ms(100);                   // some time for the USB hardware
  USB_ShutDown();                     // now disconnect
  for (uint8_t n = 0; n < 40; n++) {  // wait 4s to give host time to recognize the disconnect, found by try & error on RaspBerry Pi
    my_delay_ms(100);
  }

  USB_Init();  // now the final USB Init


Bei mir klappt es nun mit dem Reconnect, wenn ich den ergänzten BFF test ausführe (Auch in den Bootloader komme ich so noch). Daher denke ich, es scheint so zu helfen. Die 4 Sekunden habe ich empirisch ermittelt. 2 Sekunden waren zu knapp.
Daher denke ich, das wäre was, um die häufigen Fragen im Forum zu diesem Problem zu lösen. Watchdog Reset soll ja eh nur die Ausnahme und nicht die Regel sein. Da spielt die Zeit nicht so eine große Rolle.
Die 8 Sekunden Watchdog machen für Schalten nach TX mit CCA ebenfalls Sinn, da das tatsächlich schon mal länger dauert.

Gruß, Ansgar.

rudolfkoenig

Ich habe die zwei Zeilen um den Watchdog zu testen hinzugefuegt, ohne weitere IFDEFs.

Dein Problem konnte ich nicht nachvollziehen: linux und osx erkennt das Stick nachdem der Watchdog zugeschlagen hat, unter Linux muss man ein paar Sekunden warten, bis die Rechte des Geraeteknotens korrekt gesetzt werden, aber sowas sollte nicht im Firmware gefixt werden.

In deinem Patchvoschlag sind zu viele Zeilen, die ich nicht nachvollziehen kann. Ich werde nicht 20+ Dateien aendern, ohne das Problem nachstellen zu koennen, oder wenigstens den Patch verstanden zu haben..

noansi

Hallo Rudolf,

ich habe das Problem bisher nur beim CUL beobachtet, sprich pure USB Verbindung zum Atmel. Beobachtet am Raspberry Pi mit Linux. Mag sein, dass andere Systeme da unkritischer sind.

Bei COC und SCC nicht. Kann da auch nicht, da seriell verbunden und da passiert im Prinzip erst mal nichts Schlimmes an der Verbindung, wenn die CUL Firmware einen Watchdog Reset macht. Allenfalls bleibt eine Antwort auf einen gesendeten Befehl aus oder eine Message von CUL kommt nicht vollständig an, respektive wird mitten in einem Byte unterbrochen.
CUNO2 ist ein etwas anderes Thema, da CUNO/CUNO2 nicht nativ USB spricht, sondern seriell über einen zusätzlich USB Chip. Wenn dieser USB Chip einen Hänger hätte, wäre ohnehin alles zu spät, da gibt es keine Reset Möglichkeit für den Atmel Chip laut V2.1 Schaltplan. Ansonsten also genauso wie COC und SCC zu sehen. Sprich bei serieller Verbindung ist keine Änderung erforderlich.

Das schränkt die Anzahl der Dateien sehr deutlich ein, die anzupacken wären.

Es geht darum, die USB Verbindung tatsächlich zu "Disconnecten".
Bei B01 funktioniert das ja, sonst käme man nicht immer im Bootloader raus. Da gibt es ja auch zuvor ein USB_Shutdown, was das machen soll.
Allerdings muss die Zeit der Trennung im Anschluss, zumindest für den RasPi, aber auch andere Systeme ausreichend lang sein. Google förderte da eine Erfahrungsaussage eines anderen Programmierers für Windows von 500ms zu Tage. Möglicherweise (nach meiner Erfahrung offenbar) benötigt RasPi dafür noch mehr Zeit, damit Timeouts ablaufen.

Mit dem Watchdog Reset sollten die Register beim Atmel zurück gesetzt werden, das sollte auch für USB gelten. Was dabei auf dem Bus richtig oder falsch passiert, kann ich nicht sagen.
Ich habe nur per Try und Error bemerkt, dass erst ein USB_Init erforderlich zu sein scheint, damit ein folgender USB_Shutdown auch ordentlich wirkt. Und das muss dann halt in der Initialisierung des CUL passieren, gefolgt von einer ausreichend langen Wartezeit.
Wenn jemand das USB technisch genauer erklären kann, dann her mit der Erklärung. Mir kommt es nur mittlerweile so vor, als ob so einige negativ Erfahrungen mit diversen USB Geräten mit diese Problematik zusammen hängen könnten.

Mir reicht es erst mal, wenn der CUL Reconnect anschließend wieder funktioniert und nicht mein System lahm gelegt ist, weil keine Daten mehr von CUL kommen oder nichts mit CUL an Devices gesendet werden kann. Und mit dem alten Stand konnte ich es mit der BFF Ergänzung nachvollziehen, dass der Reconnect nach Watchdog Reset bei CUL scheitert. B00 hat bei mir auch erst zu einem Reconnect geführt, nachdem ich die Watchdog Zeit nach dem USB_Shutdown von den ursprünglich 15ms auf 2s und mehr hoch gesetzt hatte.

Deine Rechte-Erklärung hat bei meinem RasPi bisher nicht gepasst, denn ich habe auch in solchen Situationen schon versucht FHEM neu zu starten, aber ohne Erfolg. CUL blieb Disconneted. Nur ein linux "shutdown -r now" hat dann wieder zu Verbindung geführt oder die harte Methode mit USB device Abstecken und Anstecken. Mag auch sein, das der Linux USB-Treiber für den RasPi da nicht sauber arbeitet oder die RasPi USB Hardware?!?

Vielleicht kannst Du es dann mit dem nächsten, der über so ein Problem berichtet mit der BFF Ergänzung nachvollziehen lassen. Und ich hoffe, mein Work Around hilft auch dann.

Gruß, Ansgar.

noansi

Hallo Rudolf,

ich habe das CUL disconnect/reconnect Problem nochmal betrachtet.

Hier ein Systemlog Auszug, der das Problem zeigt:
[224195.997454] usb 1-1.3: new full-speed USB device number 15 using dwc_otg
[224196.110166] usb 1-1.3: New USB device found, idVendor=03eb, idProduct=204b
[224196.110241] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[224196.110262] usb 1-1.3: Product: CUL868
[224196.110279] usb 1-1.3: Manufacturer: busware.de
[224196.118443] cdc_acm 1-1.3:1.0: ttyACM0: USB ACM device
[224236.470355] usb 1-1.3: USB disconnect, device number 15
[224236.470749] cdc_acm 1-1.3:1.0: failed to set dtr/rts
[224239.009180] usb 1-1.3: new full-speed USB device number 16 using dwc_otg
[224239.121888] usb 1-1.3: New USB device found, idVendor=03eb, idProduct=204b
[224239.121929] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[224239.121948] usb 1-1.3: Product: CUL868
[224239.121966] usb 1-1.3: Manufacturer: busware.de
[224239.130659] cdc_acm 1-1.3:1.0: ttyACM0: USB ACM device
[224295.870410] usb 1-1.3: USB disconnect, device number 16
[224295.870829] cdc_acm 1-1.3:1.0: failed to set dtr/rts
[224298.151722] usb 1-1.3: new full-speed USB device number 17 using dwc_otg
[224298.270599] usb 1-1.3: New USB device found, idVendor=03eb, idProduct=204b
[224298.270642] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[224298.270663] usb 1-1.3: Product: CUL868
[224298.270681] usb 1-1.3: Manufacturer: busware.de
[224298.275791] cdc_acm 1-1.3:1.0: ttyACM1: USB ACM device


CUL wird mit dem falschen Port ttyACM1 statt ttyACM0 verknüpft. (mit set raw BFF provoziert)

Ergänzen muss ich, dass ich in der devio.pm DevIo_SimpleRead folgerndermassen geändert hatte, mit der Intention, ein Warten von FHEM in DevIo_SimpleReadWithTimeout zu vermeiden:
########################
# If called directly after a select, it should not block.
sub
DevIo_SimpleRead($)
{
  my ($hash) = @_;

  if (!defined($hash->{helper}{DEVIO}{NDisCon})) {
    $hash->{helper}{DEVIO}{NDisCon} = 0;
    $hash->{helper}{DEVIO}{RXfailTS} = undef;

  ##########
  # litle statistics for debugging
#    $hash->{helper}{DEVIO}{LRcN} = 0;
#    $hash->{helper}{DEVIO}{LRcNMin} = 4096;
#    $hash->{helper}{DEVIO}{LRcNMax} = 0;
  } 

  my $buf = DevIo_DoSimpleRead($hash);
  my $l = length($buf);

  my $dio = $hash->{helper}{DEVIO};

  if (defined($buf) && $l) {
    $dio->{RXfailTS} = undef;

  ##########
  # litle statistics for debugging
#    $dio->{LRcN} = $l;
#    $dio->{LRcNMin} = $l if ($l < $dio->{LRcNMin});
#    $dio->{LRcNMax} = $l if ($l > $dio->{LRcNMax});

    return $buf; # return received data
  }

  # don't let FHEM wait for data, if device does not give it after select reports data
  # but set a timeout to detect a broken connection and react
  # main loops select will bring us back here again later

  my $now = gettimeofday();

  $dio->{RXfailTS} = $now+2 if (!defined($dio->{RXfailTS})); # timeout in 2s

  if ($now > $dio->{RXfailTS}) {  # timeout read retry
    DevIo_Disconnected($hash);
    $dio->{NDisCon}++;  # for debug statistics
    $dio->{RXfailTS} = undef;
    return undef; # return undef on timeout
  }

  if($hash->{USBDev}) {
    $buf = ""; # return empty string if not timed out
  } elsif($hash->{DIODev}) {
    $buf = ""; # return empty string if not timed out
  } elsif($hash->{TCPDev}) {
    $buf = ""; # return empty string if not timed out
  }

  return $buf;
}


Das hat das eigenliche Problem stärker beleuchtet, denn mit der ursprünglichen Variante ist es nicht leicht zu provozieren.

Tritt ein Watchdog Reset auf, dann gibt es den USB disconnect und es wird von der MAIN loop readfn ausgeführt, was in DevIo_SimpleRead endet.
In Deiner ursprünglichen Fassung kommt beim DevIo_SimpleReadWithTimeout nichts und es wird Disconnect direkt ausgeführt.

In meiner Fassung dagegen wird der Disconnect 2s verzögert ausgeführt. Und das führt zu einer guten Reproduzierbarkeit (bei Firmware mit reinem USB_Init() ).
Wenn ich das richtig interpretiere, dann ist beim Reconnect von CUL ttyACM0 noch durch FHEM in Benutzung und es wird ttyACM1 vom Betriebssystem als Schnittstelle gewählt. Damit also für FHEM erst mal "unsichtbar".

Bei Deiner devio.pm Fassung kann das genauso passieren, wenn FHEM während des Watchdog Reset und USB Reconnect gerade länger beschäftigt ist und nicht in select in MAIN gerade wartet und direkt (schnell genug) reagieren kann um den Disconnect vom Port rechtzeitig auszuführen. Das macht es dann auch zu einem unangenehmen Zufallsproblem.

Der Workaround in der CUL Firmware (so weit machbar) reduziert sich damit auf mehrere Sekunden Warten vor USB_Init, um mehr Zeit für die rechtzeitige Erkennung und Schliessen der Verbindung seitens FHEM zu geben.
  // help solve USB disconnect/reconnect after watchdog reset on RasPi
  for (uint8_t n = 0; n < 50; n++) {  // wait 5s to give host time to recognize the disconnect and free the port, found by try & error on RaspBerry Pi (else a new ttyACMx device may be used instead of the disconnected one)
    my_delay_ms(100);
    wdt_reset();
  }
  USB_Init();  // now the final USB Init


Ein richtiger Fix wäre in den Untiefen vom Betriebssystem zu implementieren, so dass im Falle des USB Disconnects das geöffnete tty direkt zwangsweise geschlossen wird. Das würde dann bei allen USB devices helfen. Oder, sofern irgendwie möglich, ein Callback, der den FD in FHEM direkt schließt.

Gruß, Ansgar.

Wernieman

Wobei die Frage ist, warum Du den Port "ttyACM0" verwendest, der sich auch, z.B: durch einen Serverreboot, Ändern kann.

besser wäre eine Verwendung der dafür gedachten "Serialadresse", also z.B. bei mir:
ls -lha /dev/serial/by-id
insgesamt 0
drwxr-xr-x 2 root root 100 Mai  5 21:56 .
drwxr-xr-x 4 root root  80 Mai  5 21:56 ..
lrwxrwxrwx 1 root root  13 Mai  5 21:56 usb-busware.de_CUL433-if00 -> ../../ttyACM1
lrwxrwxrwx 1 root root  13 Mai  5 21:56 usb-busware.de_CUL868-if00 -> ../../ttyACM0
lrwxrwxrwx 1 root root  13 Mai  5 21:56 usb-FTDI_FT232R_USB_UART_A702GD6P-if00-port0 -> ../../ttyUSB0


Allerdings löst dieses nur die Symptome, das sich die Adresse (ttyACM0) Ändert, nicht das das Device sich kurz ab/an-meldet
- Bitte um Input für Output
- When there is a Shell, there is a Way
- Wann war Dein letztes Backup?

Wie man Fragen stellt: https://tty1.net/smart-questions_de.html

noansi

Hallo Wernieman,

Zitatbesser wäre eine Verwendung der dafür gedachten "Serialadresse"
danke für den Hinweis. Die links kannte ich noch nicht.

Wie vertauschungssicher ist das denn, wenn man 2 868er CULs im System nutzen möchte? Werden die nach USB-Port sortiert, an dem man das jeweilige serielle Gerät hängen hat? Die Seriennummer ist erst mal 0 für alle CULs, so lange man die nicht vor dem Compilieren und Flashen individuell im Quellcode einträgt.

Zitatnicht das das Device sich kurz ab/an-meldet
Ja, dagegen hilft nur konsequentes vermeiden des Watchdog Resets.  :)

Gruß, Ansgar.

Wernieman

Bei genau gleichen CUL ist es wirklich ein Problem. Wobei .... Wenn Du schon an der Firmware "drehst", kann man da was machen? z.B. Seriennummern ändern?

Du könntest auch über die USB-Busnummern gehen. Aber die werden nach Initialisierungszeit vergeben ...
- Bitte um Input für Output
- When there is a Shell, there is a Way
- Wann war Dein letztes Backup?

Wie man Fragen stellt: https://tty1.net/smart-questions_de.html

rudolfkoenig

@noansi:
Ich verstehe das Problem, allerdings finde ich die Loesung (immer 5 Sekunden zu warten) nicht optimal: Wer sagt es, dass 5 Sekunden reichen. Es stoert mich auch, dass dieser Patch in 10+ Dateien einzutragen ist.

Ich finde, dass den watchdog im Firmware zu vermeiden immer noch die beste Loesung ist. Scheint bei mir zu funktionieren, ich habe bei meinem "Produktion"-Culs uptimes vor ueber einem Jahr.

Bei der Analyse ist mir ein Problem aufgefallen: ich habe urspruenglich nach einem Disconnect nicht sofort ein open durchgefuehrt, sondern erst nach 5 Sekunden, da ich mit einem zu schnellen open auf einem linux Rechner mal ein Crash provoziert habe. Das war irgendwannmal verlorengegangen, aber jetzt habe ich es wieder aktiviert.

noansi

Hallo Rudolf,

Zitatallerdings finde ich die Loesung (immer 5 Sekunden zu warten) nicht optimal: Wer sagt es, dass 5 Sekunden reichen
Da stimme ich Dir voll und ganz zu.
Die Gründe für die 5s als Workaround Vorschlag waren:
Die 5s passen gut zum 8s Watchdog, den ich ohnehin bei mir gewählt hatte.
Mit 2.5s konnte ich das Problem noch bei meiner disconnect Variante mit BFF provozieren. Dabei hat dann noch der 2s Timeout, den ich in devio.pm gewählt hatte, mit gespielt.
Es ist sicherer als ohne, da die Wahrscheinlichkeit für ein nicht erreichbares CUL sinkt.

ZitatEs stoert mich auch, dass dieser Patch in 10+ Dateien einzutragen ist.
USB_Init() wird in CUN.c, CUL.c, CUR.c bei der Initialisierung verwendet. Sprich, nur bei Atmels mit nativer USB Schnittstelle.
Und noch einmal in mysleep.c, wobei dort das Warten beim Aufwachen wohl kontraproduktiv wäre?!?
Oder habe ich da was übersehen?

ZitatScheint bei mir zu funktionieren, ich habe bei meinem "Produktion"-Culs uptimes vor ueber einem Jahr.
Z.B. bei HM ist in rf_asksin.c in asksin_send Zeile 226 mit
Zitat// enable TX, wait for CCA
  do {
    ccStrobe(CC1100_STX);
  } while (cc1100_readReg(CC1100_MARCSTATE) != MARCSTATE_TX);
ein nachvollziehbarer potentieller watchdog Reset vorhanden. Bei mir habe ich dabei schon längere Wartezeiten als die 2s, die ja derzeit Watchdogzeit in der Firmware sind, beobachtet.
Wenn Du Homematic nicht oder nicht sendend benutzt (z.B nur Temperatursensoren), dann tritt das auch nicht auf.

ZitatDisconnect nicht sofort ein open durchgefuehrt, sondern erst nach 5 Sekunden
Danke für den Hinweis.
Wenn ich das richtig verstanden habe erst nach bis zu 5 Sekunden, ein früherer Timer würde die Zeit verkürzen, oder?

Die Diskussion hat mich auch darauf gebracht, das meine Änderung an DevIo_SimpleRead nicht das gelbe vom Ei ist.


@Wernieman: Beim Compilieren könnte man eine individuelle Serienummer wohl händisch einbauen. Das ist aber leider keine praktikable Lösung für alle User.

Gruß, Ansgar.

rudolfkoenig

ZitatZ.B. bei HM ist in rf_asksin.c in asksin_send Zeile 226 mit
Solche potentielle Endlos-Schleifen gefallen mir eh nicht. Vlt. sollte man eine Begrenzung auf 255 Iterationen einfuehren, und falls es nicht klappt, bei der naechsten asksin_task() wieder versuchen.

noansi

Hallo Wernieman,

ZitatBei genau gleichen CUL ist es wirklich ein Problem. Wobei .... Wenn Du schon an der Firmware "drehst", kann man da was machen? z.B. Seriennummern ändern?

Du könntest auch über die USB-Busnummern gehen. Aber die werden nach Initialisierungszeit vergeben ...

Hier ist meine Variante mit der USB Seriennummer http://forum.fhem.de/index.php/topic,24436.msg377742.html#msg377742 zu finden.

Damit habe ich für meinen RasPi das Problem der falschen Zuordnung lösen können, da dieser die USB Ports meines Hubs in umgegehrter Reihenfolge initialisiert, wenn ich ein Soft-Reboot des RasPi auslöse.

Prinzipiel ein sinnvolles Feature für die Standard Firmware.

Gruß,

Ansgar.