Arianna autonomous DAQ firmware

Dependencies:   mbed SDFileSystemFilinfo AriSnProtocol NetServicesMin AriSnComm MODSERIAL PowerControlClkPatch DS1820OW

Revision:
8:95a325df1f6b
Parent:
7:079617408fec
Child:
9:a1a39573dd43
--- a/main.cpp	Sat Aug 04 01:48:55 2012 +0000
+++ b/main.cpp	Wed Aug 08 23:27:37 2012 +0000
@@ -1,5 +1,7 @@
 #include "mbed.h"
 
+#define USE_RTOS_TIMER
+
 #include <stdint.h>
 #include "SDFileSystem.h"
 #include "MODSERIAL.h"
@@ -15,6 +17,9 @@
 #include "SnCommAfarTCP.h"
 #include "SnCommUsb.h"
 #include "SnBase64.h"
+#ifdef USE_RTOS_TIMER
+#include "RtosTimer.h"
+#endif
 
 //
 // MBED PINS (ordered by number)
@@ -81,27 +86,49 @@
 SnCommWin::ECommWinResult OpenCommWin();
 void                      MakeOutputFile(const bool stopRunning=false);
 void                      SetPower(const bool isCommWin);
+void                      procForceTrigger();
+void                      procHeartbeat();
+void                      procPowerCheck();
+void                      procCommWin();
+#ifdef USE_RTOS_TIMER
+void                      procForceTrigger(void const *) { return procForceTrigger(); }
+void                      procHeartbeat(void const *) { return procHeartbeat(); }
+void                      procPowerCheck(void const *) { return procPowerCheck(); }
+void                      procCommWin(void const *) { return procCommWin(); }
+#endif
 
 //
 // globals
 //
 // readout objs
+// TODO: use RtosTimer instead of Ticker?
+#ifdef USE_RTOS_TIMER
+static rtos::RtosTimer*     gForceTicker;
+static rtos::RtosTimer*     gHeartbeatTicker;
+static rtos::RtosTimer*     gCommWinTicker;
+static rtos::RtosTimer*     gPowerCheckTicker;
+#else
 static Ticker         gForceTicker;
 static Ticker         gHeartbeatTicker;
 static Ticker         gCommWinTicker;
+static Ticker         gPowerCheckTicker;
+#endif
 static Timer          gEvtTimer;
 static SnConfigFrame  gConf;
 static SnEventFrame   gEvent;
+static SnPowerFrame   gPower;
 // parameters
 static bool           gFirstEvt         = true;
 static bool           gReadingOut       = false;
 static bool           gCommWinOpen      = false; // if it's open
 static volatile bool  gOpenCommWin      = false; // if it should be opened
-static int32_t        gEvtNum           = 0;   // num of evt written
-static int32_t        gTrgNum[kNumTrgs] = {0}; // num of this type of trg received
+static volatile bool  gCheckPower       = false; // if it should be checked
+static uint32_t       gPowNum           = 0;
+static uint32_t       gEvtNum           = 0;   // num of evt written
+static uint32_t       gTrgNum[kNumTrgs] = {0}; // num of this type of trg received
 // i/o
 static time_t         gLastCommWin      = 0;
-static const uint32_t gBufSize=SnStatusFrame::kMaxSizeOf + SnHeaderFrame::kMaxSizeOf;
+static const uint32_t gBufSize=SnStatusFrame::kMaxSizeOf + SnHeaderFrame::kMaxSizeOf + SnPowerFrame::kMaxSizeOf;
 //static const uint32_t gB64Bsize=BASE64ENC_LEN(gBufSize)+1;
 //static char           gB64Buf[gB64Bsize];
 static char           gGenBuf[gBufSize]; // must be big enough for event or status or config!
@@ -114,6 +141,7 @@
 void procForceTrigger() {
     led3=!led3;
     if (gReadingOut==false && gCommWinOpen==false) {
+        printf("proc force\r\n");
         gEvent.SetTrgBit(kFrcTrg);
         gEvent.SetTrgNum((gTrgNum[kFrcTrg])++);
         PIN_forceTrigger = 1;     // force a trigger
@@ -122,14 +150,21 @@
 
 void procHeartbeat() {
     if (gReadingOut==false && gCommWinOpen==false) {
+        printf("proc heartbeat\r\n");
         PIN_heartbeat = 1; // heartbeat pulse
         PIN_heartbeat = 0;
     }
 }
 
+void procPowerCheck() {
+    printf("proc power\r\n");
+    gCheckPower=true;
+}
+
 void procCommWin() {
     if (gReadingOut==false && gCommWinOpen==false) {
         if ( (time(0) - gLastCommWin) > gConf.GetCommWinPeriod() ) {
+            printf("proc comm win\r\n"); 
             led3=!led3;
             gOpenCommWin = true;
         }
@@ -145,12 +180,138 @@
     return lio+startTime;
 }
 
+bool AreCardsPowered() {
+    printf("acp: PIN_turn_on_system=%d\r\n",PIN_turn_on_system.read());
+    return (PIN_turn_on_system.read()==0);
+}
 
-// TODO: add block-id's to output file? (config block, event block, file header block, etc.)
+void GetAvePowerReading() {
+    // use one measurement as the assumed average
+    // in order to reduce computational errors
+    int32_t v1, v2;
+    const uint16_t aaveV1 = PIN_vADC1.read_u16();
+    const uint16_t aaveV2 = PIN_vADC2.read_u16();
+    float n=0, ave1=0, ave2=0, rms1=0, rms2=0;
+    for (uint16_t i=0; i<kNvoltsAve; i++) {
+        v1    = PIN_vADC1.read_u16() - aaveV1;
+        v2    = PIN_vADC2.read_u16() - aaveV2;
+        n    += 1;
+        ave1 += v1;
+        rms1 += v1*v1;
+        ave2 += v2;
+        rms2 += v2*v2;
+    }
+    rms1 -= (ave1*ave1)/n;
+    rms2 -= (ave2*ave2)/n;
+    rms1 /= n-1;
+    rms2 /= n-1;
+    ave1 /= n;
+    ave2 /= n;
+    ave1 += aaveV1;
+    ave2 += aaveV2;
+    gPower.Set(ave1, ave2, rms1, rms2, time(0));
+}
 
-// TODO: HEARTBEAT!
+void CheckPower(const bool isCommWin) {
+    printf("CheckPower\r\n");
+    // read power
+    GetAvePowerReading();
+    // save to disk
+    FILE* cf = SnSDUtils::GetCurFile();
+    if (cf!=0) {
+        printf("writing power. v1=%g, v2=%g, r1=%g, r2=%g, t=%u, pownum=%u\r\n",
+            gPower.GetAveV1(), gPower.GetAveV2(), 
+            gPower.GetRmsV1(), gPower.GetRmsV2(), gPower.GetTime(),
+            gPowNum);
+        SnSDUtils::WritePowerTo(cf, gPower, gPowNum);
+    }
+    // do we need to change modes?
+    bool changed = false;
+    if (gConf.IsLowPowerMode()) {
+        if (gPower.GetAveV1() > gConf.GetBatVoltLowPwr()) {
+            printf("chaing to normal power!\r\n");
+            gConf.ChangeToNormPower();
+            changed = true;
+        }
+    } else {
+        if (gPower.GetAveV1() < gConf.GetBatVoltLowPwr()) {
+            printf("chaing to low power!\r\n");
+            gConf.ChangeToLowPower();
+            changed = true;
+        }
+    }
+    if (changed) {
+        SetPower(isCommWin);
+        printf("Using config %s\r\n",gConf.GetLabel());
+        SetConfigAndMakeOutputFile(); // setup defaults in case no communication
+    }
+    // checking done
+    gCheckPower = false;
+}
+
+bool IsSeqComplete() {
+    if (gConf.GetEvtsPerFile()>0) {
+        if (gConf.IsCountingPowerReadings()) {
+            return (gPowNum>=gConf.GetEvtsPerFile());
+        } else {
+            return (gEvtNum>=(gConf.GetFirstEvt()+gConf.GetEvtsPerFile()));
+        }
+    }
+    return false;
+}
 
-// TODO: make websocket URL settable in the config (i.e. via SBD?)
+#ifdef USE_RTOS_TIMER
+void stopTicker(rtos::RtosTimer* tik) {
+    if (tik!=0) {
+        tik->stop();
+    }
+}
+#else
+void stopTicker(Ticker& tik) {
+    tik.detach();
+}
+#endif
+
+#ifdef USE_RTOS_TIMER
+uint32_t resetTicker(rtos::RtosTimer* tik, const uint32_t timSec,
+                     const uint32_t maxTimSec) {
+    if (tik!=0) {
+        tik->stop();
+        if (timSec>0) {
+            uint32_t tp = timSec > maxTimSec ? maxTimSec : timSec;
+            tp *= 1000u; // ms
+            tik->start(tp);
+            return tp;
+        }
+     }
+     return 0;
+}
+#else
+uint32_t resetTicker(Ticker& tik, const uint32_t timSec,
+                     const uint32_t maxTimSec, void (*fptr)(void)) {
+    tik.detach();
+    if (timSec>0) {
+        const uint32_t tp = timSec > maxTimSec ? maxTimSec : timSec;
+        tik.attach(fptr, tp);
+        return tp;
+    }
+    return 0;
+}
+#endif
+
+void StopRunning() {
+    stopTicker(gForceTicker);
+    stopTicker(gHeartbeatTicker);
+    stopTicker(gCommWinTicker);
+    stopTicker(gPowerCheckTicker);
+    while (true) {
+        led3 = 1; led4=1;
+        wait(0.5);
+        led3 = 0; led4=0;
+        wait(0.5);
+        //Watchdog::kick(); - if we kick the watchdog, the station is unrecoverable without physical access
+    }
+}
 
 int main() {
     {
@@ -161,6 +322,15 @@
         led4=0;
     }
     
+    // RTOS stuff must be made inside main for some reason
+    gComms[0] = new SnCommAfarTCP(gConf);
+#ifdef USE_RTOS_TIMER
+    gForceTicker        = new rtos::RtosTimer(&procForceTrigger);
+    gHeartbeatTicker    = new rtos::RtosTimer(&procHeartbeat);
+    gCommWinTicker      = new rtos::RtosTimer(&procCommWin);
+    gPowerCheckTicker   = new rtos::RtosTimer(&procPowerCheck);
+#endif    
+    
     led2=1;
     //wait_ms(100);
     
@@ -176,7 +346,11 @@
     printf("time = %d\r\n",(int32_t)time(0));
     gLastCommWin = time(0); // prevent comm win proc
     
+#ifdef USE_RTOS_TIMER
+    gForceTicker->stop();
+#else
     gForceTicker.detach();
+#endif
     gFirstEvt = true;
     
     // (probably) power down comms and power up cards,amps
@@ -188,7 +362,7 @@
     //
     // get config
     //
-    //printf("open window\r\n");
+    printf("call OpenCommWin\r\n");
     OpenCommWin();
     
     // get ready to trigger
@@ -213,14 +387,12 @@
         WaitTrigAndSendClock();
         PIN_lockRegisters = 1; // block registers during readout
         
-        const int32_t etms = gEvtTimer.read_ms(); // time since last trigger
-        gEvtTimer.reset(); gEvtTimer.start();     // start counter from this trigger
-        
-        printf("wait trig send clock exited\r\n");
-                
-        Watchdog::kick(); // don't reset!
-        
         if (gReadingOut) {
+            const int32_t etms = gEvtTimer.read_ms(); // time since last trigger
+            gEvtTimer.reset(); gEvtTimer.start();     // start counter from this trigger
+            
+            Watchdog::kick(); // don't reset!
+            
             //
             // got trigger. read registers to mbed and build the event
             //
@@ -250,10 +422,7 @@
                 PIN_lockRegisters = 0; // done reading, unlock so we can talk to SD card.
                 
                 SaveEvent(etms);
-
-                if (gEvtNum>=(gConf.GetFirstEvt()+gConf.GetEvtsPerFile())) {
-                    MakeOutputFile(gConf.IsSingleSeqRunMode());
-                }
+                
             /*
             } else {
                 printf("forced=%s, gFirstEvt=%s, e>t %d>%hu %s\r\n",
@@ -267,6 +436,18 @@
         
         led4=0; led2=0;
         
+        // check the power?
+        if (gCheckPower) {
+            printf("call check power\r\n");
+            CheckPower(false);
+        }
+        
+        // make new seq?
+        if (IsSeqComplete()) {
+            MakeOutputFile(gConf.IsSingleSeqRunMode());
+        }
+        
+        // open comm win?
         if (gOpenCommWin) {
             printf("gOpenComWin=%s, opening\r\n",gOpenCommWin?"true":"false");
             OpenCommWin();
@@ -299,24 +480,26 @@
     // increment event number
     ++gEvtNum;
     
-    printf("gEvtNum=%d\r\n",gEvtNum);
+    printf("gEvtNum=%u\r\n",gEvtNum);
 }
 
 void MakeOutputFile(const bool stopRunning) {
+    printf("closing output file. gEvtNum=%u, gPowNum=%u\r\n",gEvtNum,gPowNum);
     SnSDUtils::CloseOutputFile(SnSDUtils::GetCurFile());
     if (stopRunning) {
-        while (true) {
-            led3 = 1; led4=1;
-            wait(0.5);
-            led3 = 0; led4=0;
-            wait(0.5);
-            Watchdog::kick();
-        }
+        StopRunning();
     }
-    SnSDUtils::OpenNewOutputFile(gConf.GetMacAddress(),
-                                 gConf.GetRun(),
-                                 PIN_vADC1.read_u16(),
-                                 PIN_vADC2.read_u16());
+    FILE* cf = SnSDUtils::OpenNewOutputFile(gConf.GetMacAddress(),
+                                            gConf.GetRun());
+    if (cf!=0) {
+        wait_ms(200);
+        GetAvePowerReading();
+        printf("writing power. v1=%g, v2=%g, r1=%g, r2=%g, t=%u, pownum=%u\r\n",
+            gPower.GetAveV1(), gPower.GetAveV2(), 
+            gPower.GetRmsV1(), gPower.GetRmsV2(), gPower.GetTime(),
+            gPowNum);
+        SnSDUtils::WritePowerTo(cf, gPower, gPowNum);
+    }
     printf("made output file with run %u\r\n",gConf.GetRun());
     printf("filename=%s\r\n",SnSDUtils::GetCurFileName());
     SnSDUtils::WriteConfig(SnSDUtils::GetCurFile(), gConf);
@@ -326,6 +509,7 @@
 // power stuff
 //
 void SetPower(const bool isCommWin) {
+    // TODO: turn on amps individually, when that's possible
     if (isCommWin) {
         PIN_turn_on_system = gConf.GetPowPinSetting(SnConfigFrame::kCardComWin);
         wait_ms(10);
@@ -345,6 +529,7 @@
         PIN_afar_power = gConf.GetPowPinSetting(SnConfigFrame::kAfarDatTak);
         wait_ms(10);
     }
+    wait(1);
     printf("set power (iscom %d, pw %hhu): cards %d, amps %d, irid %d, afar %d\r\n",
         isCommWin, gConf.GetPowerMode(), PIN_turn_on_system.read(), PIN_turn_on_amps.read(),
         PIN_iridSbd_power.read(), PIN_afar_power.read());
@@ -370,75 +555,74 @@
     // reset event, timers, trigger counters
     gEvent.ClearEvent();
     gEvtNum = gConf.GetFirstEvt();
-    memset(gTrgNum, 0, sizeof(int32_t)*kNumTrgs);
+    gPowNum = 0;
+    memset(gTrgNum, 0, sizeof(uint32_t)*kNumTrgs);
     
-    // make new output file
-    MakeOutputFile();
-    
-    // TODO: turn on amps individually, when that's possible
-    // PIN_turn_on_amps = gConf.IsEachAmpOn() ? 0 : 1;
+    if (AreCardsPowered()) {
+        // Set PLA value(s)
+        PIN_spi.format( 16, 0 ); // change mode for DAC & PLA value setting
+        PIN_spi.frequency(1000000);
+        PIN_MajLogHiBit=1;
+        PIN_MajLogLoBit=1;
+        PIN_enableThermTrig=0;
     
-    // Set PLA value(s)
-    PIN_spi.format( 16, 0 ); // change mode for DAC & PLA value setting
-    PIN_spi.frequency(1000000);
-    PIN_MajLogHiBit=1;
-    PIN_MajLogLoBit=1;
-    PIN_enableThermTrig=0;
-
-    uint16_t hi, lo;
-    PIN_PLA_cs=1;
-    wait(4);
-    for (uint8_t pi=0; pi<kNplas; pi++) {
-        if (pi < gConf.GetNumPlas()) {
-            SnConfigFrame::GetHiLoPlas(gConf.GetPla(pi), hi, lo);
-            PIN_spi.write(hi);
-            PIN_spi.write(lo);
-            printf("pla hi %hu, lo %hu\r\n",hi,lo);
-        } else {
-            PIN_spi.write(kNoTrigPla); // hi
-            PIN_spi.write(kNoTrigPla); // lo
-            printf("pla hi %hu, lo %hu\r\n",kNoTrigPla,kNoTrigPla);
+        uint16_t hi, lo;
+        PIN_PLA_cs=1;
+        wait(4);
+        for (uint8_t pi=0; pi<kNplas; pi++) {
+            if (pi < gConf.GetNumPlas()) {
+                SnConfigFrame::GetHiLoPlas(gConf.GetPla(pi), hi, lo);
+                PIN_spi.write(hi);
+                PIN_spi.write(lo);
+                printf("pla hi %hu, lo %hu\r\n",hi,lo);
+            } else {
+                PIN_spi.write(kNoTrigPla); // hi
+                PIN_spi.write(kNoTrigPla); // lo
+                printf("pla hi %hu, lo %hu\r\n",kNoTrigPla,kNoTrigPla);
+            }
+            Watchdog::kick();
         }
-        Watchdog::kick();
-    }
-    wait(3);
-    PIN_PLA_cs=0;
-    wait(3);
+        wait(3);
+        PIN_PLA_cs=0;
+        wait(3);
     
-    // DAC values
-    //
-    // first 12 bits = DAC value
-    // next 2 bits = DAC ID
-    // last 2 bits = dFPGA ID
-    //
-    // But FPGA uses "gray encoding" which means only 1 bit
-    // can change at a time (of the last 4 bits). So even tho
-    // the card/dac# is encoded, the order is also important
-    // 0000 (dac0,card0), 0001 (dac0,card1), 0011 (dac0,card3), 0010 (dac0,card2),
-    // 0110 (dac1,card2), 0111 (dac1,card3), 0101 (dac1,card1), etc.
-    printf("setting dacs\r\n");
-    uint16_t dv=0;
-    for (uint8_t i=0, gri=0; i<kTotDacs; i++) {
-        // get the gray-codes for this iteration
-        gri = SnBitUtils::binToGray(i);
-        
-        // build bit word
-        dv   = static_cast<int>(gConf.GetDac(gri & 0x0003u, gri >> 2u));
-        dv <<= 4u;
-        dv  |= gri;
-        
-        printf("dac %04x\r\n",dv);
-        
-        // send to FPGA
-        PIN_start_fpga=1;
-        PIN_spi.write(dv);
-        PIN_start_fpga=0;
-
-        Watchdog::kick();
-        
+        // DAC values
+        //
+        // first 12 bits = DAC value
+        // next 2 bits = DAC ID
+        // last 2 bits = dFPGA ID
+        //
+        // But FPGA uses "gray encoding" which means only 1 bit
+        // can change at a time (of the last 4 bits). So even tho
+        // the card/dac# is encoded, the order is also important
+        // 0000 (dac0,card0), 0001 (dac0,card1), 0011 (dac0,card3), 0010 (dac0,card2),
+        // 0110 (dac1,card2), 0111 (dac1,card3), 0101 (dac1,card1), etc.
+        printf("setting dacs\r\n");
+        uint16_t dv=0;
+        for (uint8_t i=0, gri=0; i<kTotDacs; i++) {
+            // get the gray-codes for this iteration
+            gri = SnBitUtils::binToGray(i);
+            
+            // build bit word
+            dv   = static_cast<int>(gConf.GetDac(gri & 0x0003u, gri >> 2u));
+            dv <<= 4u;
+            dv  |= gri;
+            
+            printf("dac %04x\r\n",dv);
+            
+            // send to FPGA
+            PIN_start_fpga=1;
+            PIN_spi.write(dv);
+            PIN_start_fpga=0;
+    
+            Watchdog::kick();
+            
+        }
+        printf("dacs set\r\n");
+        wait_ms(20);
+    } else {
+        printf("cards off. skipping PLA and DAC setting\r\n");
     }
-    printf("dacs set\r\n");
-    wait_ms(20);
     
     // Majority Logic Trigger selection (# of cards)
     SnBitUtils::SetChanNumBits(gConf.GetNumCardsMajLog() - 1u,
@@ -450,31 +634,49 @@
     PIN_spi.format( 16, 1 ); // back to trigger mode
     PIN_spi.frequency( 10000000 );  // Max is 12.5 MHz
 
+    // make new output file
+    // put after PLA/DAC, in case they affect the power readings
+    wait_ms(200);
+    MakeOutputFile();
+    
     // force a trigger every...
-    gForceTicker.detach();
-    if (gConf.GetForceTrigPeriod()>0) {
-        gForceTicker.attach(&procForceTrigger, 
-            gConf.GetForceTrigPeriod() > kAbsMaxTimer ?
-            kAbsMaxTimer : gConf.GetForceTrigPeriod()); // force period has a maximum
+#ifdef USE_RTOS_TIMER
+    const uint32_t ftp = resetTicker(gForceTicker, gConf.GetForceTrigPeriod(),
+                                     kAbsMaxTimer);
+    const uint32_t hbp = resetTicker(gHeartbeatTicker, gConf.GetHeartbeatPeriod(),
+                                     kAbsMaxTimer);
+    const uint32_t cwp = resetTicker(gCommWinTicker, gConf.GetCommWinPeriod(),
+                                     kCommWinLongPrdTk);
+    const uint32_t pcp = resetTicker(gPowerCheckTicker, gConf.GetVoltCheckPeriod(),
+                                     kAbsMaxTimer);
+#else
+    const uint32_t ftp = resetTicker(gForceTicker, gConf.GetForceTrigPeriod(),
+                                     kAbsMaxTimer, &procForceTrigger);
+    const uint32_t hbp = resetTicker(gHeartbeatTicker, gConf.GetHeartbeatPeriod(),
+                                     kAbsMaxTimer, &procHeartbeat);
+    const uint32_t cwp = resetTicker(gCommWinTicker, gConf.GetCommWinPeriod(),
+                                     kCommWinLongPrdTk, &procCommWin);
+    const uint32_t pcp = resetTicker(gPowerCheckTicker, gConf.GetVoltCheckPeriod(),
+                                     kAbsMaxTimer, &procPowerCheck);
+#endif
+    printf("attach force trig %u\r\n",ftp);
+    printf("attach heart beat %u\r\n",hbp);
+    printf("attach comm win   %u\r\n",cwp);
+    printf("attach power chk  %u\r\n",pcp);
+    
+    // TODO: change comm parameters
+    /*
+    printf("set comm params\r\n");
+    for (uint8_t cc=0; cc<kNcomms; cc++) {
+        if (gComms[cc]!=0) {
+            gComms[cc]->Set(gConf);
+        }
     }
-    // heartbeat every ...
-    gHeartbeatTicker.detach();
-    if (gConf.GetHeartbeatPeriod()>0) {
-        gHeartbeatTicker.attach(&procHeartbeat,
-            gConf.GetHeartbeatPeriod() > kAbsMaxTimer ?
-            kAbsMaxTimer : gConf.GetHeartbeatPeriod());
-    }
-    // proc a comm win every...
-    gCommWinTicker.detach();
-    gCommWinTicker.attach(&procCommWin,
-        gConf.GetCommWinPeriod() > kAbsMaxTimer ?
-        kCommWinLongPrdTk : gConf.GetCommWinPeriod()); // periodic check if above max
-    printf("attach comm win ticker %u\r\n",
-        gConf.GetCommWinPeriod() > kAbsMaxTimer ?
-        kCommWinLongPrdTk : gConf.GetCommWinPeriod());
+    */
     
     Watchdog::kick(); // don't reset!
-
+    
+    printf("set config done\r\n");
 }
 
 //
@@ -486,44 +688,55 @@
     printf("wait trig: (pw %hhu): cards %d, amps %d, irid %d, afar %d\r\n",
         gConf.GetPowerMode(), PIN_turn_on_system.read(), PIN_turn_on_amps.read(),
         PIN_iridSbd_power.read(), PIN_afar_power.read());
-
-    if (gFirstEvt==false) {
-        PIN_DoNotRestartAllClocks    = 0;
-        wait_us(1);
-        PIN_DoNotRestartAllClocks    = 1;
-        //led3 = !led3; // toggle send clock led
-    } else {
-        gFirstEvt = false;
-    }
+    printf("cards powered=%d\r\n",(int)AreCardsPowered());
     
-    //
-    // wait for a trigger here.
-    //
-    gReadingOut = false;  // this will allow forced triggers (see procForceTrigger())
-    while ( PIN_a_sf_clk == 1 ) {
-        if (gOpenCommWin) {
-            return; // break out to open comms
+    if (AreCardsPowered()) {
+        
+        if (gFirstEvt==false) {
+            PIN_DoNotRestartAllClocks    = 0;
+            wait_us(1);
+            PIN_DoNotRestartAllClocks    = 1;
+            //led3 = !led3; // toggle send clock led
+        } else {
+            gFirstEvt = false;
+        }
+        
+        //
+        // wait for a trigger here.
+        //
+        printf("starting wait for trig\r\n");
+        gReadingOut = false;  // this will allow forced triggers (see procForceTrigger())
+        while ( PIN_a_sf_clk == 1 ) {
+            if (gOpenCommWin || gCheckPower) {
+                printf("break com=%d, pow=%d\r\n",gOpenCommWin,gCheckPower);
+                return; // break out to open comms or check power
+            }
         }
-    }
-    PIN_forceTrigger=0;   // necessary for forced triggers, harmless for other triggers
-    gReadingOut = true;   // disallow new forced triggers
-    //
-    // collect data from daughter cards
-    //
-    // TODO: what if some card (set of channels) doesn't respond?
-    // currently, will wait forever?
-    // also, if ch1 is dead, will wait forever (due to FPGA code)
-    for( uint8_t i = 0; i < kNsamps; i++ ) {
-        if( PIN_a_sf_clk == 1 ) {
-            if( i == 0 )
-                wait_us( 1 );
-
-            PIN_ADC_CS = 0;
-            PIN_spi.write( 0x00 );
-            PIN_ADC_CS = 1;
-        } else {
-            i--;
+        printf("starting readout. force=%d, clk=%d\r\n",
+            PIN_forceTrigger.read(), PIN_a_sf_clk.read());
+        PIN_forceTrigger=0;   // necessary for forced triggers, harmless for other triggers
+        gReadingOut = true;   // disallow new forced triggers
+        //
+        // collect data from daughter cards
+        //
+        // TODO: what if some card (set of channels) doesn't respond?
+        // currently, will wait forever?
+        // also, if ch1 is dead, will wait forever (due to FPGA code)
+        for( uint8_t i = 0; i < kNsamps; i++ ) {
+            if( PIN_a_sf_clk == 1 ) {
+                if( i == 0 )
+                    wait_us( 1 );
+    
+                PIN_ADC_CS = 0;
+                PIN_spi.write( 0x00 );
+                PIN_ADC_CS = 1;
+            } else {
+                i--;
+            }
         }
+    } else {
+        // cards have no power. don't try reading out
+        gReadingOut=false;
     }
 }
 
@@ -546,6 +759,7 @@
     
     // close the file so that the data is all written out.
     // and open it back up at the beginning (for reading)
+    printf("close & open file. gEvtNum=%u, gPowNum=%u\r\n",gEvtNum,gPowNum);
     SnSDUtils::CloseOutputFile(SnSDUtils::GetCurFile());
     SnSDUtils::OpenExistingFile(SnSDUtils::GetCurFileName(), true);
     
@@ -580,8 +794,10 @@
         printf("calling OpenWindow. ss=%d\r\n",(int)(*ss));
         printf("gtt=%u, ct=%d, lcw=%d, dur=%u\r\n",GetTimeoutTime(gLastCommWin,conto),
             time(0), gLastCommWin, gConf.GetCommWinDuration());
+        // update power reading in case we want to send it in status
+        GetAvePowerReading();
         const SnCommWin::ECommWinResult conres = (*cw)->OpenWindow(
-            GetTimeoutTime(gLastCommWin, conto), *ss, gConf, gEvent, gGenBuf);
+            GetTimeoutTime(gLastCommWin, conto), *ss, gConf, gEvent, gPower, gGenBuf);
         if (conres>=SnCommWin::kConnected) {
             Watchdog::kick(); // don't reset!
             // connected. listen for config
@@ -590,19 +806,14 @@
                 gConf, GetTimeoutTime(gLastCommWin, listo), gGenBuf, gBufSize);
             if (cfgres>=SnCommWin::kOkWithMsg) {
                 Watchdog::kick(); // don't reset!
-                printf("received config (%u)!\r\n",gConf.SizeOf());
+                printf("received config!\r\n");
                 char* b = gGenBuf;
                 gConf.WriteTo(b);
-                const uint32_t csz = gConf.SizeOf();
-                for (uint32_t i=0; i<csz; i++) {
-                    printf("%02x ",gGenBuf[i]);
-                }
-                printf("\r\n");
                 // send data if need be (files, some events, etc)
                 printf("send data = %d\r\n", gConf.GetCommSendData());
                 if (gConf.GetCommSendData()!=0) {
                     printf("sending data\r\n");
-                    res = (*cw)->SendData(gConf, gEvent, gGenBuf, gBufSize,
+                    res = (*cw)->SendData(gConf, gEvent, gPower, gGenBuf, gBufSize,
                         GetTimeoutTime(gLastCommWin, gConf.GetCommWinDuration()));
                 } else {
                     // don't send anything