Arduino Asksin library

Begonnen von trilu, 06 August 2013, 10:02:17

Vorheriges Thema - Nächstes Thema

Dietmar63


ZitatZitat
Also virtuelle Methoden sind auf dem 8bit AVR sehr teuer - sprich benötigen relativ viel Code und auch Daten
Ich habe das Mal am pcb Sketch getestet. Ist etwas übersichtlicher geworden, hat aber auch 600 Byte gekostet. Das ist zu viel...

ist das nur hinsichtlich der verbrauchten Bytes ein Problem oder auch von der Laufzeit her ein Problem?
Ich war schon lange nicht mehr auf Systemen unterwegs, auf denen man bytes sparen muss.

Man könnte ja den Versuch unternehen die ChannelModel genutzen Methoden nicht per virtual zu beschreiben, sondern als Delegate-Klasse zu nutzen:


call ChannelModel::regInHM(...)
call ChannelModel::sonstwiefunktion(...)


Dann sit das ganze Übersichtlicher und muss nicht in jeder cmChannelKlasse als Kopie vorliegen.
Dem ChannelModell müßte man dann die triggerFunktionen als callback mitgeben.
Gruß Dietmar
FB7390, CUL, 2 FHT, FS20
modules: 98_WOL.pm, 98_Heating_Control.pm,   98_WeekdayTimer.pm, 98_RandomTimer.pm, 59_Twilight.pm

trilu

Laufzeit ist egal, zumindest ist mir nichts aufgefallen. Das Problem ist die Code Größe.
Der PCB Sketch ist derzeit schon bei 70% Nutzung. Wenn jetzt noch ein 2 Tasten Remote dazukommt sind wir vermutlich bei 80%.
Wenn der 2 Tasten Remote dann auch noch Touch sein soll, brauchen wir noch die I2C Funktionen, naja, und irgendwann ist Speicher Ende :-)

Dietmar63

Ok, verstanden.

Was verursacht den umfangreichen Code.
Sind das Bibliotheken, die zugebunden werden, oder ist es AS und die Schwesterklassen?

Eine Schwesterklasse Channelmodel sollte noch drin sein vor allem dann, wenn mehre cmChannelmodelle den gleichen Code teilen können.

Apropos:
Wenn HM_LC_SW1_BA_PCB scheinbar anlernbar ist, müsste ich dann on/off schalten können. Ich glaube ich bekomme immer "missing acknoledge".

Vielleicht findet ihr in Papas Ansatz noch interessante Lösungen die dann in NewAskSin einfließen. Die Sache mit den channel Definitionen habe ich auch noch nicht gefressen.
Gruß Dietmar
FB7390, CUL, 2 FHT, FS20
modules: 98_WOL.pm, 98_Heating_Control.pm,   98_WeekdayTimer.pm, 98_RandomTimer.pm, 59_Twilight.pm

trilu

ich vermute das die codegröße aus dem überschreiben von einigen virtual functions kommt. den rest hatte ich ja soweit bereinigt, also wirklich vergleichbar gemacht.
kannst ja mal testen, habe die virtual class sache hochgeladen...
ansonsten, ja, wenn du pairen kannst muss auch remote schalten gehen.

papa

#1159
Zitat von: trilu am 30 August 2016, 07:57:00
@papa - ich muss mir Mal deinen Ansatz genauer anschauen.
Wie machst du in deinem Ansatz das Konfiguration der Channel Module?

Im Prinzip wird alles durch Zusammenstecken der Templates konfiguriert. Den Rest macht dann der Compiler - erlöst alles auf. Z.B. die List1 des Switches aus meiner Testimplementierung. Den Start macht die SwitchList1Data Klasse. Sie definiert das Datenlayout der Liste sowie die Mapping-Funktionen von Offset zu Register und umgekehrt. Die Datenmember könnte man sich auch noch sparen, aber ich mache lieber sizeof() als selber rechnen. Die eigentliche Liste wird duch das Anlegen der ChannelList<SwitchList1Data> definiert. Im Prinzip kommt hier nur die EEProm-Adresse rein, bei welcher die Listedaten liegen. Außerdem gibt es hier die Methoden zu Lesen und Schreiben der Daten im EEProm. Ein Channel wird dann durch den Listen1Typ, den Listen3Typ sowie die Anzahl der Peers definiert. Außerdem kommt noch die Statemachine dazu, die immer switchState() aufruft, wenn eine Zustandsänderung gemacht wird. Das wird dann überladen, um die Pins zu schalten.


class SwitchChannel : public Channel<SwitchList1,SwitchList3,4>, public SwitchStateMachine


Das Device wird wieder mit den ChannelDatenTyp und die Anzahl der Channel definert. Als Argument kommt die Startadresse im EEProm. Von hier aus werden alle EEPromAdressen berechnet.


MultiChannelDevice<SwitchChannel,4> switchdev(0x20);


Derzeit geht das Pairen und einfache Schalten schon. Bei einem 4 Channel Switch komme ich auf folgende Code/Daten Größen:


Program:   12746 bytes (38.9% Full)
(.text + .data + .bootloader)
Data:        584 bytes (28.5% Full)
(.data + .bss + .noinit)


Ist also noch ganz gut Luft - aber auch noch nicht alles drin. So ich hoffe, hiermit kann man den Code (leider noch voll unkommentiert) besser verstehen.
BananaPi + CUL868 + CUL433 + HM-UART + 1Wire

Dietmar63

@ trilu:

ich versuche  über die Funktion pci_callback(vec, bInt, pcint_vector_byte[vec].curr & bInt);
den Vector, den Pin und die Funktion falling/raising/... herauszufinden.

Kann es sein, dass die Funktion maintainPCINT nicht funktioniert?

Das Ergebnis für den gleichen Pin variert:
Für PIN_C0  bekomme ich:

cb, vec:1, pin:0, flag:0
cb, vec:1, pin:2, flag:2
cb, vec:1, pin:0, flag:0
cb, vec:1, pin:2, flag:2
cb, vec:1, pin:0, flag:0
cb, vec:1, pin:2, flag:2
cb, vec:1, pin:0, flag:0
cb, vec:1, pin:2, flag:2
cb, vec:1, pin:0, flag:0
cb, vec:1, pin:2, flag:2


und für PIN_C1 kommt


cb, vec:1, pin:3, flag:2
cb, vec:1, pin:3, flag:2
cb, vec:1, pin:3, flag:2
cb, vec:1, pin:3, flag:2
cb, vec:1, pin:2, flag:2
cb, vec:1, pin:2, flag:2
cb, vec:1, pin:3, flag:2
cb, vec:1, pin:2, flag:2
cb, vec:1, pin:3, flag:2
cb, vec:1, pin:2, flag:2
cb, vec:1, pin:2, flag:2


vec scheint zu stimmen, pin und flag sehen komisch aus.


void    maintainPCINT(uint8_t vec) {
pcint_vector_byte[vec].curr = *pcint_vector_byte[vec].PINR  & pcint_vector_byte[vec].mask; // read the pin port and mask out only pins registered
pcint_vector_byte[vec].time = getMillis(); // store the time, if debounce is asked for

#ifdef PCINT_CALLBACK // callback only needed if defined in hardware.h
uint8_t bInt = pcint_vector_byte[vec].curr ^ pcint_vector_byte[vec].prev; // evaluate the pin which raised the interrupt
pci_callback(vec, bInt, pcint_vector_byte[vec].curr & bInt); // callback the interrupt function in user sketch
#endif
}
Gruß Dietmar
FB7390, CUL, 2 FHT, FS20
modules: 98_WOL.pm, 98_Heating_Control.pm,   98_WeekdayTimer.pm, 98_RandomTimer.pm, 59_Twilight.pm

Dietmar63

ich habe es jetzt ersteinmal so gemacht:


void PCint(uint8_t port) {
  uint8_t bit;
  uint8_t curr;
  uint8_t mask;
  uint8_t pin;

  // get the pin states for the indicated port.
  curr = *portInputRegister(port+2);
  // dbg << "curr: " << curr << "\n";
 
  mask = curr ^ PCintLast[port];
  // dbg << "mask: " << mask << "\n";
 
  PCintLast[port] = curr;
  // mask is pins that have changed. screen out non pcint pins.
  if ((mask &= *port_to_pcmask[port]) == 0) {
    return;
  }
  // mask is pcint pins that have changed.
  for (uint8_t i=0; i < 8; i++) {
    bit = 0x01 << i;
    if (bit & mask) {
      pin = port * 8 + i;
      pin = i;
      dbg << "port: " << port << " pin: " << pin << " rising " << ((curr & bit) == 0) << "\n";
     
      #ifdef PCINT_CALLBACK // callback only needed if defined in hardware.h
pci_callback(port, pin, ((curr & bit) == 0)); // callback the interrupt function in user sketch
      #endif
     
     
     
      }
    }
  }
/**
* @brief This function is called from the different interrupt vector functions while an interrupt
* had happened. Within this function we check also if there is a callback for an interrupt registered.
*
* @param vec    Is the vector byte, were the interrupt comes from
*/
void    maintainPCINT(uint8_t vec) {
        PCint(vec);
pcint_vector_byte[vec].curr = *pcint_vector_byte[vec].PINR  & pcint_vector_byte[vec].mask; // read the pin port and mask out only pins registered
pcint_vector_byte[vec].time = getMillis(); // store the time, if debounce is asked for

//#ifdef PCINT_CALLBACK // callback only needed if defined in hardware.h
//uint8_t bInt = pcint_vector_byte[vec].curr ^ pcint_vector_byte[vec].prev; // evaluate the pin which raised the interrupt
//pci_callback(vec, bInt, pcint_vector_byte[vec].curr & bInt); // callback the interrupt function in user sketch
//#endif
}
//- -----------------------------------------------------------------------------------------------------------------------
void pci_callback(uint8_t vec, uint8_t pin, uint8_t flag) {
   char pinString [3];
   
   // hm.pw.stayAwake(100);

   pinString[0] = 'B' + vec;
   pinString[1] = '0' + pin;
   pinString[2] = 0;
   dbg << "cb, vec:" << vec << ", pin:" << pin << ", flag:" << flag << " PIN_" << pinString << '\n';
}


das ist das Ergebnis:

port: 0 pin: 0 rising 1
cb, vec:0, pin:0, flag:1 PIN_B0
port: 1 pin: 0 rising 1
cb, vec:1, pin:0, flag:1 PIN_C0
port: 1 pin: 1 rising 1
cb, vec:1, pin:1, flag:1 PIN_C1
Gruß Dietmar
FB7390, CUL, 2 FHT, FS20
modules: 98_WOL.pm, 98_Heating_Control.pm,   98_WeekdayTimer.pm, 98_RandomTimer.pm, 59_Twilight.pm

trilu

Eigentlich funktioniert sie schon, sie hat nur mehr Flags

* This functions returns a 3 for a raising edge, 2 for a falling edge, 1 if no interrupt had happend and the pin is high
* and a 0 if the pin is low.

Dietmar63

Ich versuche beide Algorithmen zu verstehen.
Vielleicht funktioniert sie ja, nur ich habe noch nicht geblickt wie. 
Gruß Dietmar
FB7390, CUL, 2 FHT, FS20
modules: 98_WOL.pm, 98_Heating_Control.pm,   98_WeekdayTimer.pm, 98_RandomTimer.pm, 59_Twilight.pm

Dietmar63

#1164
Zitat von: Dietmar63 am 04 September 2016, 07:05:42
Ich versuche beide Algorithmen zu verstehen.
Vielleicht funktioniert sie ja, nur ich habe noch nicht geblickt wie.

hab's versucht, bin aber noch nicht sicher ob ich es verstanden habe.

in in der callback funktion kommt dies an. Welcher pin hat dann wie gefeuert?
Wie bekomme ich es aus den Parametern heraus?


cb, vec:1, pin:1, flag:1
cb, vec:1, pin:3, flag:1
cb, vec:1, pin:3, flag:1
cb, vec:1, pin:1, flag:1
cb, vec:1, pin:1, flag:1
cb, vec:1, pin:3, flag:1
cb, vec:1, pin:1, flag:1
cb, vec:1, pin:1, flag:1
cb, vec:1, pin:3, flag:1
cb, vec:1, pin:1, flag:1
cb, vec:1, pin:1, flag:1
cb, vec:1, pin:3, flag:1
cb, vec:1, pin:1, flag:1
cb, vec:1, pin:1, flag:1


registriert habe ich C0 und C1.
Gruß Dietmar
FB7390, CUL, 2 FHT, FS20
modules: 98_WOL.pm, 98_Heating_Control.pm,   98_WeekdayTimer.pm, 98_RandomTimer.pm, 59_Twilight.pm

kadettilac89

Hi,

ich möchte ein bestehendes Modul anpassen und finde zu einem Teil nichts ...

Irgendwo werden die Devicetypes definiert damit diese dann in FHEM erkannt werden. Im bestehenden Modul sehe ich das hier ...


# $HMConfig::culHmModel{'F101'} = {name => 'HB-UW-Sen-THPL-I', st => 'THPLSensor', cyc => '00:10', rxt => 'l:w:c:f', lst  => 'p',   chn  => '',};
$HMConfig::culHmModel{'F201'} = {name => 'HB-UW-Sen-TH-Pn', st => 'THPnSensor', cyc => '00:10', rxt => 'w:c', lst  => 'p',   chn  => '',};


Es ist in HMConfig_SenTH.pm von LineF enthalten, ist aber eine allgemeine Frage.

Wo muss ich das definieren wenn ich mir z. B. ein HB-UW-Sen-THP-Pn mit F301 bauen möchte? Gibt es hierzu eine Anleittung oder ein Template in dem das gut dokumentiert ist?

Danke im Voraus!

Linef

Meines Erachtens wird das meiste in der Datei selber definiert (und nur ein Teil aus HMConfig.pm übernommen).
Ein weiterer Teil scheint dann in 10_CUL_HM.pm hart codiert zu sein (z.B. bei einem RT-DN)...

Hier die Grundparameter des Devices:

$HMConfig::culHmModel{'F201'} = {name => 'HB-UW-Sen-TH-Pn', st => 'THPnSensor', cyc => '00:10', rxt => 'w:c', lst  => 'p',   chn  => '',};


Hier dann die Register - soweit sie nicht bereits in der HMConfig.pm definiert sind

$HMConfig::culHmRegDefine{'lowBatLimitTH'} = {a=>18.0,s=>1.0,l=>0,min=>1.0,max=>5,c=>'',f=>10,u=>'V',d=>0,t=>'Low batterie limit, step 0.1 V.'};
$HMConfig::culHmRegDefine{'oscCal'} = {a=>35.0,s=>1.0,l=>0,min=>0,max=>255,c=>'',f=>'',u=>'',d=>0,t=>'OSCCAL: calibration value of controller frequency'};


Dann noch, welche Register das Device haben soll.
Wobei - da gebe ich Dir schon recht - ich auch nicht weiß, wo die restlichen "Standard"-Register her kommen; es sind ja tatsächlich mehr, als hier gelistet (zumindest das pairCentral)

# Register model mapping
$HMConfig::culHmRegModel{'HB-UW-Sen-TH-Pn'} = {
'burstRx'         => 1,
'lowBatLimitTH'   => 1,
'ledMode'         => 1,
'transmDevTryMax' => 1,
'oscCal'          => 1
};


Hmmm, ist das hier nur für die "Description" da?

# subtype channel mapping
$HMConfig::culHmSubTypeSets{'THPnSensor'}    = {
'peerChan'       => '0 <actChn> ... single [set|unset] [actor|remote|both]',
'fwUpdate'       => '<filename> <bootTime> ...',
'getSerial'      => '',
'getVersion'     => '',
'statusRequest'  => '',
'burstXmit'      => ''
};


Und dann kommt die Funktion, die die übertragenen Bytes wieder auseinanderpfriemelt...

# Subtype spezific funtions
sub CUL_HM_ParseTHPnSensor(@){

my ($mFlg, $frameType, $src, $dst, $msgData, $targetDevIO) = @_;
...


Wobei mir bislang noch nicht klar ist, wie ich jetzt Register pro Peer definieren kann.
Im Device habe ich sie schon mal angelegt, aber wie muss ich sie hier definieren, damit ich sie dann auch per Kommando setzen kann?

Vielleicht kann ja martinp876 hier uns einen Tip geben?

Martin
fhem auf cubietruck, HM-USB-CFG-2, CUL-V3, 6x HM-CC-RT-DN, 5x HM-SEC-SD, 2x HM-SEC-SCo, 5x HM Eigenbausensoren, AVR-Heizungsgateway

kadettilac89

Zitat von: Linef am 10 September 2016, 21:55:23
Meines Erachtens wird das meiste in der Datei selber definiert (und nur ein Teil aus HMConfig.pm übernommen).
Ein weiterer Teil scheint dann in 10_CUL_HM.pm hart codiert zu sein (z.B. bei einem RT-DN)...

Hier die Grundparameter des Devices:

$HMConfig::culHmModel{'F201'} = {name => 'HB-UW-Sen-TH-Pn', st => 'THPnSensor', cyc => '00:10', rxt => 'w:c', lst  => 'p',   chn  => '',};



Hi Martin,

vielen Dank für die ausführliche Antwort! Ich dachte ich muss das culHmModel noch irgendwo (device-xml?) definieren. Scheinbar nicht.

Wollte dein Modul auf BME280-Sensor umbauen und ein eigenes THP-Modul bauen. Mehr um das ganze zu verstehen, könnte natürlich auch das F101-Modul vom Wettersensor (trilu?) nehmen und einfach Helligkeitswert ignorieren.

Naja, der BME280 streut mehr als 2°C nach oben, Illusion alles klein und mit einem genauen Temp-Sensor aufzubauen erstmal weg. Bin enttäuscht da der BME-Sensor im Internet wegen der Genauigkeit hoch gelobt wird. Gibt etliche Foren in denen das behandelt wird, scheinbar müsste der Sensor neu kalibriert werden wenn er zu heiß gelötet wurde (Reflow).

kadettilac89

Zitat von: Tom71 am 04 Juni 2016, 10:51:41

- Kompilieren mit Arduino IDE für "Arduino Pro or Pro Min, 3.3V, 8Mhz)
- Umwandeln der hex-> eq3
- Flashen per OTA

Dann kommt auf der Console:
AskSin OTA Bootloader V0.7.0

Start App ˜,¿­§¿£ÍÞ¿¬�¿ Þ�ãÓ£ÑÞ]œÑ


Die Kompiler-Params sind:

avr-g++" -c -g -Os -w -std=gnu++11 -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics  -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=8000000L -DARDUINO=10609 -DARDUINO_AVR_PRO -DARDUINO_ARCH_AVR   "-IC:arduino" "-IC:eightanaloginputs" "HM-Sensor-DS.ino.cpp" -o "nul"


Hi Tom71,

hast du das OTA hinbekommen? Wenn ja, welchen Boatloader und nach welcher Anleitung hast du ihn geflasht?

Dietmar63

#1169
Um besser das Verhalten eines Arduinos hinsichtlich WDT und powerDown verstehen zu können habe ich mir folgendes kleines TestProgramm gebaut:


/*

*/

#include <PinChangeInt.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

#define offBrownOut()           MCUCR = (1<<BODS)|(1<<BODSE); MCUCR = (1<<BODS);
#define backupPwrRegs()         uint8_t xPrr = PRR; PRR = 0xFF;
#define recoverPwrRegs()        PRR = xPrr;

const int testLed    = 3;

const int ledPin     = 13;
const int pirPin     = 14;

int   WDTtime = 0;
long  tim = 0;

volatile int pir;
volatile int wdt;
         int wdtold = -1;

void wdtEnable (uint8_t period, uint8_t interrupt) {

  wdt_enable(period);
  if (interrupt == 1) {
    WDTCSR |= (1 << WDIE);   
    Serial.print("setting interrupt: ");     
    Serial.print(interrupt);     
    Serial.println("");
    Serial.flush();
  }
  WDTtime = 2 << (period + 3); // in Millisekunden
}

 
void setup() {
   Serial.begin(57600);
   Serial.print("starting pirWDTtest");   
   Serial.print("\tms:\t");
   Serial.print(millis());   
   Serial.println("");
   Serial.flush();
     
   pinMode(ledPin,    OUTPUT);
   
   pinMode     (pirPin, INPUT);                           //set the pin to input
   PCintPort::attachInterrupt(pirPin, pirCount, RISING); // attach a PinChange Interrupt to our pin on the rising ed
   //delay(500); 

   sei();

}

void loop(){
 
   unsigned long now = millis();
   if (wdt != wdtold) {
      Serial.print(" pircount:\t");
      Serial.print(pir);
      Serial.print("\twdtcount:\t");
      Serial.print(wdt);       
      Serial.print("\tms:\t");
      Serial.print(tim);
      Serial.print("\mod:\t");
      Serial.print(now % 1000);
      Serial.print(" ");
      Serial.print(WDTtime);
      Serial.println("");
   }
   Serial.flush();
   
   ADCSRA = 0;                                 // disable ADC
// #################
  if (wdt != wdtold) {
   if (wdt>0 && wdt%5 == 0) {
      //wdtEnable (1, 0);
      wdtEnable (9, 1);
   } else {
      wdtEnable (9, 1);  // 16*2^period ms als interrupt       
   }
  }
 
  wdtold = wdt;
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  cli();
  if (1)
  {
    sleep_enable();
    sleep_bod_disable();
    sei();
    sleep_cpu();
    sleep_disable();
  }
  sei();

  Serial.print("... wieder wach");
  Serial.println("");
  Serial.flush();

// #################   
}

void pirCount()   
   { pir++;  /* voltAus(); */ digitalWrite(ledPin, HIGH); delay(10); digitalWrite(ledPin, LOW);}

ISR(WDT_vect)
{
   wdt++;
   tim += WDTtime;
   // wdt_reset();          // reset watchdog counter
   // WDTCSR |= (1<<WDIE);  // reenable interrupt to prevent system reset

   // WDIE & WDIF is cleared in hardware upon entering this ISR
   // wdt_disable();

   
}


Es startet den  WDT-Timer des A. für 8 (Code 9) Sekunden und vesetzt ihn in den SLEEP_MODE_PWR_DOWN Zustand.
Ohne PIN-Change-Interrupt passiert das Erwartete. Der Arduino wacht alle 8192 ms auf und gibt über Serial die eine Zeile mit den Variablen aus.

Wenn ich aber den  PIN-Change-Interrupt  (Zeile 75) aktiviere passiert etwas Unerwartetes: Wenn über pirPin ein Interrupt ausgelöst wird, wacht der A. auf(ok!), arbeitet den Interrupt über die Routine picCount() ab, legt sich dann aber nicht!!! schlafen, sondern fährt mit der Hauptschleife loop() fort und gibt unerwarteter Weise eine Zeile aus und startet den timer neu. Das lässt sich nur mit der kleinen (schon eingebauten) Sonderlogik über wdtold abfangen.

Ist dieses Verhalten bekannt?
Wenn das immer so ist, bedeutet das, dass wenn man im (New)AskSin Interrupts verarbeitet, die Zeitmessungen auch über getMillis() nicht stimmt:
Ein Interrupt wird irgendwann ausgelöst, die bis dahin vergangene Zeit wird nicht berechnet aber  der Timer wird neu gestartet. Im Mittel müssten pro Interrupt bei 8s WDTs 4 Sekunden verloren gehen.

Oder befinde ich mich mit meinen Vermutungen irgendwie auf einem Holzweg?
Gruß Dietmar
FB7390, CUL, 2 FHT, FS20
modules: 98_WOL.pm, 98_Heating_Control.pm,   98_WeekdayTimer.pm, 98_RandomTimer.pm, 59_Twilight.pm