Publish issue within state machine --- not printing cycle 3?

I have a state machine…In the header file I have a simple #define TEST. If this is in the state machine sends print to the COMM port…If this is remarked out I send print to Publish. You can see from the post that the prints are pretty much the same. The issue is Publish will print Cycle 1 and Cycle 2 then start back over, never printing Cycle 3, however the Comm will print Cycle 1, Cycle 2 and then Cycle 3. You can see on the first print line the ternary logic it is IDENTICAL. Furthermore I show the state machine logic prior to the PUBLISH calls and you can see it is nothing but an #ifdef. Also I am careful to place a 1 second delay in the WEB_PUBLISH state so as not to overwhelm publishing. Can someone tell me why Cycle 3 does NOT work in the WEB_PUBLISH state??? The only call to either state is made in the READING state which I posted. The final point to note is readCycle exists nowhere else.

          case READING:
            for (current = 0; current < 4; current++){
              DAC_Load_Value(dacValues[current]);
              float* pPublish = &SensorDataPerCyclePublish.Vbias[0];
              for (uint8_t i = 0; i < NUMBER_SAMPLES; i++){
                  ReadingArray[0][i] = analogRead(Vbias);
                  ReadingArray[1][i] = analogRead(SiPM_current);
                  ReadingArray[2][i] = analogRead(LED1_current);
                  ReadingArray[3][i] = analogRead(LED2_current);
              }
              SensorDataPerCyclePublish.w_temp[current] = ReadWaterTemp();
              SensorDataPerCyclePublish.bd_temp[current] = ReadIntTemp();
              for (uint8_t arr = 0; arr < NUMBER_SENSORS; arr++){
                float k = GetAverage((&ReadingArray[arr][0]), NUMBER_SAMPLES);
                *(pPublish + current + 4*arr) = k; //stuff struct t_PublishData
              }
            }
            readCycle++;
            sm = SOLENOID_POWER;
            break;
          case SOLENOID_POWER:
            if (readCycle < 3){
              if (!msmntFlush.isActive()){
                msmntFlush.start();
#ifdef TEST
                sm = COMM_PUBLISH;
#else                
                sm = WEB_PUBLISH;
#endif                
              }

              if (enables.expiry_waterFlush){
                msmntFlush.stop();
                enables.expiry_waterFlush = false;
                printFlag = false;
                sm = READING;
              }
              break;
            }
            else{
              readCycle = 0;
              printFlag = false;
              enables.m_waitPeriod = false;
#ifdef TEST
              sm = COMM_PUBLISH;
#else                
              sm = WEB_PUBLISH;
#endif                
            }
          case COMM_PUBLISH:
            for (current = 0; current < 4; current++){
                Serial.printlnf("CYCLE %d ------------------------- CURRENT %0.1f mA", ((readCycle > 0) ? readCycle : 3), DAC_C[current]);
                Serial.printlnf("Vbias %0.2f", SensorDataPerCyclePublish.Vbias[current]);
                Serial.printlnf("SiPM %0.2f", SensorDataPerCyclePublish.SiPMCurrent[current]);
                Serial.printlnf("LED1 %0.2f", SensorDataPerCyclePublish.LED1Current[current]);
                Serial.printlnf("LED2 %0.2f", SensorDataPerCyclePublish.LED2Current[current]);
                Serial.printlnf("WaterT %0.2f", SensorDataPerCyclePublish.w_temp[current]);
                Serial.printlnf("BoardT %0.2f", SensorDataPerCyclePublish.bd_temp[current]);
            }
            if (readCycle != 0)              
              sm = SOLENOID_POWER;
            else{
              Serial.printlnf("--------EVENT FINISHED------------");
              Serial.printlnf("----------------------------------");
              sm = SOLENOID_NOPWR;
            }
            break;    
          case WEB_PUBLISH:
            for (current = 0; current < 4; current++){
              Particle.publish("---Cycle/Current/Time----", String::format("%d, %0.1f, %lu", ((readCycle > 0) ? readCycle : 3), DAC_C[current], millis()));
              Particle.publish("Vbias/SiPM/LED1/LED2/Tw/Tbd",String::format("%0.2f, %0.2f, %0.2f, %0.2f, %0.1f, %0.1f", SensorDataPerCyclePublish.Vbias[current], \
                SensorDataPerCyclePublish.SiPMCurrent[current], SensorDataPerCyclePublish.LED1Current[current], SensorDataPerCyclePublish.LED2Current[current], \
                SensorDataPerCyclePublish.w_temp[current],SensorDataPerCyclePublish.bd_temp[current]));
              delay(1000);
            }
            if (readCycle != 0)              
              sm = SOLENOID_POWER;
            else{
              sm = SOLENOID_NOPWR;
            }

Actually you are not really.
You publish twice but only delay once.
Hence you will run into the rate limit after the fourth publish - by that time you have published four events in two seconds and hence the fifth publish will be blocked.

Two publishes would require two seconds to keep the publish rate at one per one second.

Strangely enough I am not seeing any issues with the publishing up to Cycle 3. Meaning it is publishing 2 pieces every second and it does this 4x over successfully. Waits 20 seconds, repeats again successfully and waits “unknown time — greater than 20 seconds” and I see Cycle 1 again. Never Cycle 3 can you comment on my above question regarding why the ternary operator works with comms but not with the publish?

The reference docs will help solve that puzzle too

Your burst go through, till they don't anymore and then you have a cool down phase after which the first few can go through again but never all of them.

If you only want to redirect the output I'd suggest you keep the other code exactly the same or even better have one code do the preparation for the output and then only use that so prepared data either way.

something like this

         case PUBLISH: {
             char msg[1024];
             for ( current = 0; current < 4; current++ ) {
               snprintf(msg, sizeof(msg)
                 , "Cycle [mA]  Vbias   SiPM   LED1   LED2     Tw    Tbd       [ms]\r\n"
                   "%-4d %5.1f %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f %10lu"
                 , ((readCycle > 0) ? readCycle : 3)
                 , DAC_C[current]
                 , SensorDataPerCyclePublish.Vbias[current]
                 , SensorDataPerCyclePublish.SiPMCurrent[current]
                 , SensorDataPerCyclePublish.LED1Current[current]
                 , SensorDataPerCyclePublish.LED2Current[current]
                 , SensorDataPerCyclePublish.w_temp[current]
                 , SensorDataPerCyclePublish.bd_temp[current]
                 , millis()
               );
#ifdef TEST
               Serial.println(msg);
#else
               Particle.publish("readings", msg);
               delay(1000);
#endif
             }
             sm = (readCycle ? SOLENOID_POWER : SOLENOID_NOPWR);
         }
         break;

BTW, your pointer arithmetic here

could do with some clarification.
For one I'm surprised the pointer assignment didn't throw an error (it may have thrown a warning) since your seem to be assigning a *float[] pointer *float which should be incompatible in C++.
Given the definition here

I think it should be

float* pPublish = &SensorDataPerCyclePublish.Vbias[0][0];

If the above should not be the definition for sensorDataPerCyclePublish could you make it a habit to also provide the definitions of any non-trivial variables (as we already pointed out in multiple other discussions with you)?

Also - providing above definition does fit - I'd propose a more readable and clearer syntax for populating your data fields

const int SENSORS_COUNT  = 3;
const int CURRENTS_COUNT = 4;
...
  float (*pPublish)[CURRENTS_COUNT] = SensorDataPerCyclePublish.Vbias[0]; // should not be redefined inside loop over and over as it won't change between iterations anyhow
  for (current = 0; current < CURRENTS_COUNT; current++) {
    ...
    for (uint8_t sensor = 0; sensor < SENSORS_COUNT; sensor++){
      float k = GetAverage((&ReadingArray[sensor][0]), NUMBER_SAMPLES);
      pPublish[sensor][current] = k; // clearer than the "out of order" offsets 
    }
  }

It would also be advisable to have your array dimensions dynamic (use constants instead of anonymous numbers - as in my code above) to keep any reference to them consistent.
e.g. with

  for (current = 0; current < 4; current++) {
     ...
    for (uint8_t arr = 0; arr < NUMBER_SENSORS; arr++){
      float k = GetAverage((&ReadingArray[arr][0]), NUMBER_SAMPLES);
      *(pPublish + current + 4*arr) = k; //stuff struct t_PublishData
    }
  }

(i.e. is that 4 in 4*arr the same as the 4 in the outer loop? Or is NUMBER_SENSORS == 4)
You have a hard relationship between NUMBER_SENSORS and 4*arr and pPublish which comes with some ambiguity just reading the code and moreover will cause problems anytime you may decide to change one of these but forget to also apply the change to all entities related to that change.
Particularly without explicit validity checks where your pointer arithmetic will point to this is a recipe for trouble.

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.