Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[msom] Support burnin tests #2704

Merged
merged 4 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 159 additions & 3 deletions user/applications/tinker/src/burnin_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ BurninTest::BurninTest() {
&particle::BurninTest::testSram,
&particle::BurninTest::testSpiFlash,
&particle::BurninTest::testCpuLoad,
#if PLATFORM_ID == PLATFORM_MSOM
&particle::BurninTest::testCellularModem,
&particle::BurninTest::testGnss,
#endif
};

test_names_ = {
Expand All @@ -63,7 +67,11 @@ BurninTest::BurninTest() {
"BLE_SCAN",
"SRAM",
"SPI_FLASH",
"CPU_LOAD"
"CPU_LOAD",
#if PLATFORM_ID == PLATFORM_MSOM
"CELL_MODEM",
"GNSS"
#endif
};
}

Expand All @@ -77,7 +85,7 @@ BurninTest* BurninTest::instance() {

void BurninTest::setup(bool forceEnable) {
if (!forceEnable) {
hal_pin_t trigger_pin = D7; // PA27 aka SWD
hal_pin_t trigger_pin = SWD_DAT; // PA27 P2: D7 MSOM: D27
// Read the trigger pin for a 1khz pulse. If present, enter burnin mode.
pinMode(trigger_pin, INPUT);
uint32_t pulse_width_micros = pulseIn(trigger_pin, HIGH);
Expand All @@ -94,7 +102,13 @@ void BurninTest::setup(bool forceEnable) {
}
}

logger_ = std::make_unique<Serial1LogHandler>(115200, LOG_LEVEL_INFO);
Particle.disconnect();

LogCategoryFilters burninFilters = {
{ "ncp.at", LOG_LEVEL_TRACE },
{ "app", LOG_LEVEL_INFO }
};
logger_ = std::make_unique<Serial1LogHandler>(115200, LOG_LEVEL_INFO, burninFilters);

// Detect if backup SRAM has a failed test in it (IE state is "TEST FAILED")
Log.info("BURN IN START: ResetReason: %d State: %d ErrorMessage: %s", System.resetReason(), (int)BurninState, BurninErrorMessage);
Expand Down Expand Up @@ -459,5 +473,147 @@ bool BurninTest::testCpuLoad() {
return true;
}

#if PLATFORM_ID == PLATFORM_MSOM

static bool turnModemOn() {
Cellular.on();
waitFor(Cellular.isOn, 30000);

if (!Cellular.isOn()) {
strcpy(BurninErrorMessage, "Cell modem failed to turn on after 30s");
return false;
}
return true;
}

bool BurninTest::testCellularModem() {
if (!turnModemOn()) {
return false;
}

CellularDevice device = {};
device.size = sizeof(device);
if (cellular_device_info(&device, NULL)) {
strcpy(BurninErrorMessage, "Failed to get cellular info");
return false;
}

Log.info("Cell modem ICCID: %s IMEI: %s FW: %s", device.iccid, device.imei, device.radiofw);

Cellular.off();
waitFor(Cellular.isOff, 30000);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to check if it turns off? Maybe add turnModemOff()? It could also not explicitly fail the test with false if it doesn't turn off.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not required that the modem be off for the test to be considered successful. This is just an attempt to maintain state after the test is complete. If the modem fails to power on for subsequent cell/gnss tests, that is a legitimate failure though.

return true;
}

static int callbackGPSGGA(int type, const char* buf, int len, bool* gnssLocked) {
// EXAMPLE:
// $<TalkerID>GGA,<UTC>,<Lat>,<N/S>,<Lon>,<E/W>,<Quality>,<NumSatUsed>,<HDOP>,<Alt>,M,<Sep>,M,<DiffAge>,<DiffStation>*<Checksum><CR><LF>
// +QGPSGNMEA: $GPGGA,213918.00,3804.405282,N,12209.922544,W,1,05,3.0,145.9,M,-26.4,M,,*5E

//Log.info("%s", buf);

*gnssLocked = false;

const int MAX_GPGGA_STR_LEN = 256;
char gpggaSentence[MAX_GPGGA_STR_LEN] = {};
scott-brust marked this conversation as resolved.
Show resolved Hide resolved
strlcpy(gpggaSentence, buf, MAX_GPGGA_STR_LEN);

String lattitudeLongitude("LAT/LONG:");
int numberSattelites = 0;

const char * delimiters = ",";
char * token = strtok(gpggaSentence, delimiters);
int i = 1;
while (token) {
//Log.trace("%d %s", i, token);
token = strtok(NULL, delimiters);
i++;

switch (i) {
case 3: // Lattitude or checksum if no lock and field is empty
if (strlen(token) > 5) {
lattitudeLongitude.concat(" ");
lattitudeLongitude.concat(token);
}
break;
case 4: // N/S
case 5: // Longitude
case 6: // E/W
lattitudeLongitude.concat(" ");
lattitudeLongitude.concat(token);
break;
case 8: // Number satellites
numberSattelites = (int)String(token).toInt();
if (numberSattelites > 0) {
*gnssLocked = true;
Log.info("%s Satellites: %d", lattitudeLongitude.c_str(), numberSattelites);
}
break;
default:
break;
}
}

return 1;
}

bool BurninTest::testGnss() {
// Turn on GNSS + Modem
pinMode(GNSS_ANT_PWR, OUTPUT);
digitalWrite(GNSS_ANT_PWR, HIGH);

if (!turnModemOn()) {
return false;
}

// Enable GNSS. It can take some time after the modem AT interface comes up for the GNSS engine to start
const int RETRIES = 10;
int r = 0;
for (int i = 0; i < RETRIES && r != RESP_OK; i++) {
r = Cellular.command("AT+QGPS=1\r\n");
delay(1000);
}

if (r != RESP_OK) {
strcpy(BurninErrorMessage, "AT+QGPS=1 failed, GNSS not enabled");
return false;
}

// Configure antenna for GNSS priority
for (int i = 0; i < RETRIES && r != RESP_OK; i++) {
r = Cellular.command("AT+QGPSCFG=\"priority\",0");
delay(1000);
}

if (r != RESP_OK) {
strcpy(BurninErrorMessage, "AT+QGPSCFG=\"priority\",0 failed, GNSS not prioritized");
return false;
}

// Poll NMEA GGA sentence
// Parse for satellite lock bit, parse rough lat/long + print it
bool gnssLocked = false;
const int GNSS_POLL_TIMEOUT_MS = 90000;
auto timeout = millis() + GNSS_POLL_TIMEOUT_MS;
while (millis() < timeout && !gnssLocked) {
Cellular.command(callbackGPSGGA, &gnssLocked, 1000, "AT+QGPSGNMEA=\"GGA\"");
Log.info("gnssLocked %d", gnssLocked);
delay(1000);
}

// If no lock in X minutes, fail
if (!gnssLocked) {
strcpy(BurninErrorMessage, "No GNSS lock after 90s");
}

// Turn off Cell modem + GNSS antenna
digitalWrite(GNSS_ANT_PWR, LOW);
Cellular.off();
waitFor(Cellular.isOff, 30000);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

turnModemOff() ? It could also not explicitly fail the test with false if it doesn't turn off.

return gnssLocked;
}

#endif

} // particle
#endif // HAL_PLATFORM_RTL872X
4 changes: 4 additions & 0 deletions user/applications/tinker/src/burnin_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ class BurninTest {
bool testSram();
bool testSpiFlash();
bool testCpuLoad();
#if PLATFORM_ID == PLATFORM_MSOM
bool testCellularModem();
bool testGnss();
#endif
};

}
11 changes: 6 additions & 5 deletions user/applications/tinker/src/fqc_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,8 @@ bool FqcTest::bleScan(JSONValue req) {
#if PLATFORM_ID == PLATFORM_P2
static Vector<uint16_t> p2_gpio_test_pins = { A5, S6, WKP, S4, D0, D1, S0, S1, S2, A1, S3, D2, D4, D5, D3, A0, A2, S5 };
static Vector<uint16_t> photon2_gpio_test_pins = {A0, D10, A1, A2, A5, D5, S4, D4, S3, D3, SCK, D2, MOSI, SCL, MISO, SDA };
#else
static Vector<uint16_t> p2_gpio_test_pins = { };
static Vector<uint16_t> photon2_gpio_test_pins = { };
#elif PLATFORM_ID == PLATFORM_MSOM
static Vector<uint16_t> msom_gpio_test_pins = { D2, D3, D4, D5, D6, D7, D8, D11, D12, D13, D22, D23, D25, D26, D21, D20, A0, A1, A2, A3, A4, A7 };
#endif

static Vector<uint16_t> gpio_test_pins = {};
Expand Down Expand Up @@ -358,9 +357,11 @@ bool FqcTest::ioTest(JSONValue req) {
Log.info("Hardware Model: 0x%X Variant: %lu", (unsigned int)model, variant);
}

#if PLATFORM_ID == PLATFORM_P2
#if PLATFORM_ID == PLATFORM_P2
gpio_test_pins = variant == PLATFORM_P2_PHOTON_2 ? photon2_gpio_test_pins : p2_gpio_test_pins;
#endif
#elif PLATFORM_ID == PLATFORM_MSOM
gpio_test_pins = msom_gpio_test_pins;
#endif

for(int i = 0; i < gpio_test_pins.size(); i+=2){
pinA = gpio_test_pins[i];
Expand Down