Skip to content

readBlockData() sometimes times out after 200ms: New test code #29

@RonW144

Description

@RonW144

I started this issue just to point out that I recently added some test code to the old issue readBlockData() sometimes times out after 200ms. It had been a couple of months since anyone last posted to that issue, so I'm thinking it was assumed inactive. Everything I'm posting here is also posted to the original issue.

Anyhow, here's a recap of the original issue:
We have 7 analog input modules on our P1AM-100 system:

  • Power supply: P1-01AC on 120VAC
  • Shield: P1AM-SERIAL with nothing connected.
  • CPU: P1AM-100 with USB serial to a PC
  • Module 1: P1-04AD
  • Module 2: P1-04RTD
  • Modules 3-7: P104THM
  • Module 8: P1-08TRS (Relay outputs)

The following code usually works, but occasionally times out after 200ms. It does this even if we have nothing attached to any modules.

char buffer[112];
P1.readBlockData(buffer,112,0,ANALOG_IN_BLOCK);

This is with Arduino IDE 2.3.5 and P1AM 1.0.9.

Here's the new test code. This is an exact copy/paste from the *.ino file. On my system, this fails 3-5 times per hour.
Notes:

  • The switch on the P1AM-100 is in the up position.
  • Nothing is attached to any of the I/O modules.
  • The P1AM-100 is powered directly from 120VAC in my office cube.
  • I commented out #define DEBUG_PRINT_ON in defines.h, as described in the comment on line 50.
//! @file       P1AM-100_Test.ino

// Use debugging print statements. (Comment out to turn off print statements.)
#define TESTER_DEBUG

// Debugging print statements.
#ifdef TESTER_DEBUG
#define DEBUGPRINT(VALUE...) do {Serial.print(VALUE);} while(0)
#define DEBUGPRINTLN(VALUE...) do {Serial.println(VALUE);} while(0)
#else
#define DEBUGPRINT(VALUE...) do {} while(0)
#define DEBUGPRINTLN(VALUE...) do {} while(0)
#endif

#include <P1AM.h>
#include <P1AM_Serial.h>

const int JUNKCOUNT = 1000;
unsigned long junk[JUNKCOUNT];

void ConfigureIO(void)
{
  // P1-04AD: https://facts-engineering.github.io/modules/P1-04AD/P1-04AD.html

  // Enable ch 1-4, 0-10V, 0-20mA, 0-20mA, 0-10V.
  const char P1_04AD_CONFIG_1[] = { 0x40, 0x03, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x22, 0x03, 0x00, 0x00, 0x23, 0x01 };

  // P1-04RTD: https://facts-engineering.github.io/modules/P1-04RTD/P1-04RTD.html

  // Enable ch 1-4, High Side Burnout, Fahrenheit, Pt1000, 33Hz/16bit/61ms.
  const char P1_04RTD_CONFIG_2[] = { 0x40, 0x03, 0x60, 0x07, 0x20, 0x02, 0x80, 0x00 };

  // P1-04THM: https://facts-engineering.github.io/modules/P1-04THM/P1-04THM.html

  // Enable ch 1-4, High Side Burnout, Fahrenheit, Type J, J, J, J.
  const char P1_04THM_CONFIG_3[] = { 0x40, 0x03, 0x60, 0x07, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  // Enable ch 1-4, High Side Burnout, Fahrenheit, Type J, J, K, K.
  const char P1_04THM_CONFIG_4[] = { 0x40, 0x03, 0x60, 0x07, 0x21, 0x00, 0x22, 0x00, 0x23, 0x01, 0x24, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  // Enable ch 1-4, High Side Burnout, Fahrenheit, Type T, T, T, T.
  const char P1_04THM_CONFIG_5[] = { 0x40, 0x03, 0x60, 0x07, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05, 0x24, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  // Enable ch 1-4, High Side Burnout, Fahrenheit, Type T, T, T, T.
  const char P1_04THM_CONFIG_6[] = { 0x40, 0x03, 0x60, 0x07, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05, 0x24, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  // Enable ch 1-4, High Side Burnout, Fahrenheit, 0-1.25V, 0-1.25V, 0-1.25V, 0-1.25V.
  const char P1_04THM_CONFIG_7[] = { 0x40, 0x03, 0x60, 0x07, 0x21, 0x0e, 0x22, 0x0e, 0x23, 0x0e, 0x24, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

  // P1-08TRS: https://facts-engineering.github.io/modules/P1-08TRS/P1-08TRS.html

  // No configuration for P1-08TRS.

  // Note: comment out #define DEBUG_PRINT_ON in defines.h so P1.init() won't print a list of modules to the serial port.
  // On Windows machines, defines.h is probably at %HOME%\Documents\Arduino\libraries\P1AM\src.
  while (!P1.init())
  {
    ;  // Wait for Modules to Sign on.
  }

  // Configure modules.
  P1.configureModule(P1_04AD_CONFIG_1, 1);   // Config data to the module in slot 1.
  P1.configureModule(P1_04RTD_CONFIG_2, 2);  // Config data to the module in slot 2.
  P1.configureModule(P1_04THM_CONFIG_3, 3);  // Config data to the module in slot 3.
  P1.configureModule(P1_04THM_CONFIG_4, 4);  // Config data to the module in slot 4.
  P1.configureModule(P1_04THM_CONFIG_5, 5);  // Config data to the module in slot 5.
  P1.configureModule(P1_04THM_CONFIG_6, 6);  // Config data to the module in slot 6.
  P1.configureModule(P1_04THM_CONFIG_7, 7);  // Config data to the module in slot 7.

  return;
} // ConfigureIO


#ifdef __arm__
// should use uinstd.h to define sbrk but Due causes a conflict
extern "C" char* sbrk(int incr);
#else  // __ARM__
extern char *__brkval;
#endif  // __arm__

int freeMemory()
{
  char top;
#ifdef __arm__
  return &top - reinterpret_cast<char*>(sbrk(0));
#elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151)
  return &top - __brkval;
#else  // __arm__
  return __brkval ? &top - __brkval : &top - __malloc_heap_start;
#endif  // __arm__
}

//! @brief Recursive function, to use the stack.
unsigned long recursion(int levelsToGo)
{
  unsigned long timeStamp = millis();

  if (0 == levelsToGo)
  {
    return timeStamp;
  }

  return timeStamp + recursion(levelsToGo - 1);
}


//! @brief Setup code, to run once at startup.
void setup()
{
  // Set LED to be a digital output.
  pinMode(LED_BUILTIN, OUTPUT);

  // Turn on the LED.
  digitalWrite(LED_BUILTIN, true);

  // Initialize and configure I/O modules in the base.
  ConfigureIO();

  // Open serial communications.
  Serial.begin(115200);

  // Wait for USB serial port to connect.
  while (!Serial)
  {
    ;  // Wait for serial port to connect. Needed for native USB port only.
  }

  DEBUGPRINT("Available memory = ");
  DEBUGPRINTLN(freeMemory());

  return;
} // setup


//! @brief Loop code, to run repeatedly after setup is complete.
void loop()
{
  bool analogFailure = false;

  const int ANALOGINPUTBLOCKLEN = 4*4*7;  // 4 bytes/channel, 4 channels/module, 7 modules.
  char analogInputData[ANALOGINPUTBLOCKLEN];  // Data for block read of analog modules.

  // Use switch to blink LED.
  digitalWrite(LED_BUILTIN, (millis()%500) < 250); // Blink LED at 2Hz.

  for (int i = 0; i < JUNKCOUNT; i++)
  {
    junk[i] = millis();
  }

  recursion(100); // Use the stack.

  // Read data as a block.
  unsigned long timeStamp_ms = millis(); // We want to see how long the calls to readBlockData() take.
  P1.readBlockData(analogInputData,16,0,ANALOG_IN_BLOCK);
  unsigned long duration_ms_1 = millis();
  P1.readBlockData(analogInputData + 16,16,16,ANALOG_IN_BLOCK);
  unsigned long duration_ms_2 = millis();
  P1.readBlockData(analogInputData + 32,16,32,ANALOG_IN_BLOCK);
  unsigned long duration_ms_3 = millis();
  P1.readBlockData(analogInputData + 48,16,48,ANALOG_IN_BLOCK);
  unsigned long duration_ms_4 = millis();
  P1.readBlockData(analogInputData + 64,16,64,ANALOG_IN_BLOCK);
  unsigned long duration_ms_5 = millis();
  P1.readBlockData(analogInputData + 80,16,80,ANALOG_IN_BLOCK);
  unsigned long duration_ms_6 = millis();
  P1.readBlockData(analogInputData + 96,16,96,ANALOG_IN_BLOCK);
  unsigned long duration_ms_7 = millis();
  unsigned long duration_ms = duration_ms_7 - timeStamp_ms; // Duration of all readBlockData() calls.
  // Each "duration" is currently just a timestamp.
  // Subtract consecutive timestamps to get the actual durations.
  duration_ms_7 -= duration_ms_6; // Duration of module 7 readBlockData() call.
  duration_ms_6 -= duration_ms_5; // Duration of module 6 readBlockData() call.
  duration_ms_5 -= duration_ms_4; // Duration of module 5 readBlockData() call.
  duration_ms_4 -= duration_ms_3; // Duration of module 4 readBlockData() call.
  duration_ms_3 -= duration_ms_2; // Duration of module 3 readBlockData() call.
  duration_ms_2 -= duration_ms_1; // Duration of module 2 readBlockData() call.
  duration_ms_1 -= timeStamp_ms;  // Duration of module 1 readBlockData() call.

  // See if the calls to readBlockData() took too long.
  if (50 < duration_ms)
  {
    // Print debug message if the failure just happened, not if it's ongoing.
    if (!analogFailure)
    {
      DEBUGPRINT("readBlockData total "); DEBUGPRINT(duration_ms); DEBUGPRINTLN(" ms");
      DEBUGPRINT("readBlockData parts "); DEBUGPRINT(duration_ms_1); DEBUGPRINT(" ");
      DEBUGPRINT(duration_ms_2); DEBUGPRINT(" "); DEBUGPRINT(duration_ms_3); DEBUGPRINT(" ");
      DEBUGPRINT(duration_ms_4); DEBUGPRINT(" "); DEBUGPRINT(duration_ms_5); DEBUGPRINT(" ");
      DEBUGPRINT(duration_ms_6); DEBUGPRINT(" "); DEBUGPRINT(duration_ms_7); DEBUGPRINTLN(" ms");

      // The call to readBlockData() took too long. P1 modules must be reconfigured.
      analogFailure = true;
    }
  }

  // Recover if base loses power or analog input failure.
  if((P1.isBaseActive() == false) || analogFailure)
  {
    DEBUGPRINTLN("Base lost power or analog failure.");
    DEBUGPRINT("Timestamp: "); DEBUGPRINT(millis()); DEBUGPRINTLN(" ms");
    while (!P1.init())
    {
      DEBUGPRINTLN("Waiting for power;");
      delay(500);
    }

    // Power has been restored to the base.
    DEBUGPRINTLN("Power returned;");

    // Set the LED to match the switch state.
    digitalWrite(LED_BUILTIN, digitalRead(SWITCH_BUILTIN));

    // Initialize and configure I/O modules in the base.
    ConfigureIO();
    DEBUGPRINTLN("Analog modules configured");
    analogFailure = false;
  } // Base lost power or analog input failure.

  // Read all serial data from USB serial port.
  while (Serial.available())
  {
    Serial.read();
  }

  return;
} // loop

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions