Skip to content

Feat WiFi creds over serial#283

Merged
nitin710 merged 17 commits intomasterfrom
feat-sdCardWiFiCredentials
Mar 26, 2024
Merged

Feat WiFi creds over serial#283
nitin710 merged 17 commits intomasterfrom
feat-sdCardWiFiCredentials

Conversation

@nitin710
Copy link
Collaborator

@nitin710 nitin710 commented Sep 14, 2023

Description

  • Adds provision to write WiFi credentials to config file over serial input
  • Fixes issue where any serial input key could activate Debug mode

Requirements

Issues Referenced

Documentation update

Notes for Reviewer

  • For details about handshaking, see SD Card Serial Add Wifi Credentials API Diagram drawing in drive.
  • sample output
Successfully entered config file edit mode.
@FV,1.9.0~
Options available in this mode:
1. Add a wifi credential. Usage: @WA,{"ssid":"SSSS","password" : "PPPP"}~
  - Replace SSSS with network name and PPPP with network password
2. List wifi credentials. Usage: @LS,~
3. Delete Wifi Credential. Usage: @WD,<credential number>~
  - use List option to get a wifi list. Then use the number in the list to delete a credential

>>>>>>>>>>>[serial input] @WA,{"ssid":"emoti-net","password" : "emotibit-rocks"}~
typetag: WA
payload: {"ssid":"emoti-net","password" : "emotibit-rocks"}
Parsed output: 
ssid: emoti-net
password: emotibit-rocks


config file exists. Appending to file.
@AK,WA~
Updated file contents:
{
  "WifiCredentials": [
    {
      "ssid": "emoti-net",
      "password": "emotibit-rocks"
    }
  ]
}


>>>>>>>>>>>[serial input] @LS,~
typetag: LS
payload: 
##################################
config file credetials:
0. emoti-net : emotibit-rocks
1. SSSS : PPPP
##################################
@AK,LS~


>>>>>>>>>>>[serial input] @WD,1~
typetag: WD
payload: 1
Deleting entry: 1. SSSS
@AK,WD~

Testing

Results

  • Synopsis of results
Test Feather ESP32 Feather M0
Add credentials when config.txt does not exist ✔️ ✔️
Add credentials when config.txt fails parsing ✔️
Add cred, config exists, can be parsed ✔️ ✔️
delete valid option ✔️ ✔️
delete invalid option ✔️ ✔️
add enterprise cred ✔️ ✔️

Feature Tests

  • [✔️ ] WiFi creds serial interface: Add cred, config not exist
  • [✔️ ] WiFi creds serial interface: Add cred, config exists, cannot be parsed
  • [✔️ ] WiFi creds serial interface: Add cred, config exists, can be parsed
  • [✔️ ] WiFi creds serial interface: Add cred, config exists, can be parsed, more than 12 creds
  • [✔️ ] WiFi creds serial interface: delete valid option, config exists, can be parsed
  • [✔️ ] WiFi creds serial interface: delete invalid option, config exists, can be parsed
  • [✔️ ] WiFi creds serial interface: delete option, config does not exists
  • [✔️ ] WiFi creds serial interface: Add enterprise cred, config exists, can be parsed

Steps to test

  • Use the serial monitor to edit credentials in the config file
  • Handy test inputs
    • Add a credential: @WA,{"ssid":"ssid_name","password" : "network_password"}~
    • Delete a credential: @WD,<number>~
    • List creds: @LS,~

Shared files

Checklist to allow merge

  • All dependent repositories used were on branch master
  • Firmware
    • Set testingMode to TestingMode::NONE
    • Set const bool DIGITAL_WRITE_DEBUG = false (if set true while testing)
    • Update version in EmotiBit.h
    • Update library.properties to the correct version (should match EmotiBit.h)
  • doxygen style comments included for new code snippets
  • Required documentation updated

@nitin710
Copy link
Collaborator Author

nitin710 commented Sep 26, 2023

WIP

  • ✔️ Needs a clearer Serial interface
  • Add
    • ✔️ details on options available when the mode is entered
    • [stretch goal] move Sd functions to a new file?
    • ✔️ "pretty" the output when using list command
    • output the new config file contents when a delete is performed, (same as add)

@nitin710
Copy link
Collaborator Author

nitin710 commented Sep 26, 2023

Moving this provision to a different class.

  • Class name options
    • EmotiBitSdController
      • failure cases
        • If storatge is not an Sd-Card
          • NAND flash or
          • EEPROM
          • FRAM
    • EmotiBitStorageController // As per discussion, storage might be a superset of NVM. a better word needed

Current functions getting affected

{
  bool setupSdCard(int chipSelPin, bool loadConfig);
  bool loadConfigFile(String filename); // need to pass wifi object handle
  bool writeSdCardMessages(File &dataFile, String &stringToWrite);
  void printConfigInfo(File &file, String &stringToWrite)
  void processWiFiConfigInputs(String &configFileName);
}

New class

class UserAccessibleStorage
{
  bool setup(enum hwVersion, string sku); 
  bool loadConfigFile(String filename, EmotiBitWiFi &emotibitWifi); 
  bool writeToStorage(File &dataFile, String &stringToWrite);
  void writeConfigInfo(File &file, String &stringToWrite);
  void processWiFiConfigInput(String &configFileName);
  bool createDataFile(String filename);
}

@nitin710
Copy link
Collaborator Author

Discussion points

  • storage is a superset of NVM
  • Removing the word "EmotiBit"
  • Removing the word "controller"

@nitin710
Copy link
Collaborator Author

nitin710 commented Oct 6, 2023

possible stored data types

  • exposed to user
    • config file
    • data csv file
    • data info file
    • setup log
    • ?Derivative metric typetags (defined by users)
    • Derivative metric pipelines
    • sampling rates
    • base filename modifier
    • device name
    • disabe/enable sensors sets
    • disable/enable typetags
    • crash event logging
    • debug log
  • emotibit private
    • hardware serial number/identification
    • Sensor calib data
      • EDA
    • hardware specific sensor-constellation profile
      • maybe this is hardcoded in the EEPROM not if/else in code?
    • crash core dump

Organizing into categories

  • DataWriter (class)
    • writeCsv
    • write Info
  • Data Reader (class) // future implementation. Probably plays with implementing reading data files from SD-Card using WiFi
    • readCsv
    • readInfo
  • ConfigurationManager (decoupled from other classes like DataWriter)(class)
    • config File (currently)
      • in the future, we may split this into
        • loadWifiCreds (exclusively used by EmotiBitWiFi)
        • processConfigWiFiInputs (called buy EmotiBit)
          • emotibit.configurationManager.processWiFiCredsInputs()
    • Derivative metric typetags (defined by users)
    • Derivative metric pipelines
    • sampling rates
    • base filename modifier
    • device name
    • disabe/enable sensors sets
    • disable/enable typetags
    • hardware serial number (emotibit private)
    • sensor calib (emotibit private)
    • crash core dump (emotibit private)
    • hardware sensor-contellation profile (emotibit private)
  • Logging (class)
    • setup log
    • Debug log
    • Crash log

Summary

  • There is a bit more to be thought through to identify how these classes (defined above) will interact with other classes in EmotiBit and the information exchange.
    • The information exchange will dictate the structure of files
  • A high level discussion on this topic may also bring out other types not thought of yet.

Conclusions

  • We should shelf this PR till we define the above listed classes better.
    • The pros for doing so
      • higher likelihood of not moving code again after we complete the PR
      • relatively low tax on task switching as this is a very defined code add.
      • It is very consolidated and well documented, so picking it up later should be relatively easy.
      • It is not an "essential" feature add.
    • The cons for doing so
      • we will leave an open PR
      • This is a good feature add to have, and keep testing to gauge if it needs to change/evolve. (needs more time in master)

Next steps

  • discuss and define the structure mentioned above.
  • Define the above classes in more detail + information flow

Copy link
Collaborator

@produceconsumerobot produceconsumerobot left a comment

Choose a reason for hiding this comment

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

@nitin710 code review complete

{
char input;
input = Serial.read();
if (input == 'R')
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's the reasoning for moving restartMcu() out of the first serial.available() block?
As I recall this was added to enable enterprise wifi compatibility. Are we sure moving it still covers that purpose?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I moved it to consolidate serial inputs to one location.
This shouldn't affect the enterprise wifi, because the reset option is still above the sensor setup and WiFi setup. But, I did not test it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thinking more about, there is no reason we have a reset option downstream in the code.

If I weigh the pros and cons of having promts together in code, vs possibly breaking enterprise, preserving enterprise makes more sense.

So, I will add it back to this place. Will add a comment on why its there.

EmotiBit.cpp Outdated
if (Serial.available() >= 0)
{
char c = Serial.read();
if (c == 'C')
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems like we need 'C' (and perhaps the other serial inputs) to be defined as e.g. Inputs::CREDS in EmotiBitSerial.h so that we don't have magic chars littering the code

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed. I liked your namespace.
SO changed letters to EmotiBitSerial::Input::CRED_UPDATE

EmotiBit.cpp Outdated
timeSinceLastPrint = millis();
if (configFileError)
{
Serial.println("Press \"C\" to add/update cofig file.");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Serial.prints should probably also refer to EmotiBitSerial Inputs::CREDS or similar to avoid magic chars lying around

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated.

Serial.println("{\"WifiCredentials\": [{\"ssid\":\"SSSS\",\"password\" : \"PPPP\"}]}");
// ToDo: verify if we need a separate case for FACTORY_TEST. We should always have a config file, since FACTORY TEST is a controlled environment
setupFailed("Config file not found");
if(loadConfig)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a reason that this loadConfig block doesn't live inside EmotibitConfigManager?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Moved it to ConfigManager. Created a bit of a hassle, but its done!

@@ -0,0 +1,287 @@
#include <EmotiBitConfigManager.h>

bool EmotiBitConfigManager::init(uint8_t sdCsPin)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm confused why we have/need to have SD.begin() inside EmotiBitConfigManager. It seems like makes it more spaghetti-like and less clear who's in charge of calling SD.begin and thus makes it more likely it getting called multiple times, which I'm not 100% sure what the behavior would be. Can we simply require sdInit() to be called within EmotiBit.cpp prior to calling updateWiFiCredentials?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed. As discussed, EmotiBIt now inits SD and passes pointer to ConfigManager class

file = SD.open(configFilename);
parseError = deserializeJson(configAsJson, file);
file.close();
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems like perhaps the code logic could be simplified if(!fileExists || parseError) was handled here by creating new file with zero creds?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed the logic. Yes, i bubbled if(!fileExists || parseError) logic upstream. i think it's more readable now

configAsJson["WifiCredentials"].remove(deleteIndex);
SD.remove(configFilename);
#if defined ARDUINO_FEATHER_ESP32
file = SD.open(configFilename, FILE_WRITE);
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems like this SD.open + serialJsonPretty is a copy/paste of code above with poorer error handling. Perhaps it should be pulled out int a function?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, created a new function to handle file creation from JsonElement

}
else
{
// send list of existing credentials
Copy link
Collaborator

Choose a reason for hiding this comment

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

This comment seems out of place/perhaps vestigial.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Outdated comment. removing.

Copy link
Collaborator

@produceconsumerobot produceconsumerobot left a comment

Choose a reason for hiding this comment

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

@nitin710 testing review complete

EmotiBitSerial::sendMessage(EmotiBitFactoryTest::TypeTag::FIRMWARE_VERSION, emotibitFwVersion);
// instructions
Serial.println("Options available:");
Serial.println("1. Add a wifi credential. Usage: @WA,{\"ssid\":\"SSSS\",\"password\" : \"PPPP\"}~");
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think perhaps it could be clearer to format the instructions as:

  1. Add a wifi credential -- Send: @Wa,{"ssid":"SSSS","password" : "PPPP"}~

if(configAsJson["WifiCredentials"].size())
{
Serial.println("##################################");
Serial.println("config file credetials:");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add n to credetials

Copy link
Collaborator

Choose a reason for hiding this comment

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

Capital c in Config

}
else
{
Serial.println("not a valid serial message. Expecting @TYPETAG,PAYLOAD~");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Capital N in not

Copy link
Collaborator

Choose a reason for hiding this comment

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

Add a : to Expecting: @TYPETAG,PAYLOAD~

// Send EmotiBit Firmware version to host
EmotiBitSerial::sendMessage(EmotiBitFactoryTest::TypeTag::FIRMWARE_VERSION, emotibitFwVersion);
// instructions
Serial.println("Options available:");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should the Options available be printed after every command so that you don't have to scroll back up to see the options?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I played with it, i personally didn't like it. Too much going on the screen.

@nitin710
Copy link
Collaborator Author

@produceconsumerobot made the requested changes. Ready for review. (long list of changes.)

Copy link
Collaborator

@produceconsumerobot produceconsumerobot left a comment

Choose a reason for hiding this comment

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

It would probably be good if I test it one more time if you want to drop a bin file in the PR, but code looks much nicer.

#ifdef ARDUINO_FEATHER_ESP32
bool EmotiBitConfigManager::init(fs::SDFS* sd)
{
SD = sd;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I might have chosen a variable name that doesn't overload SD to avoid confusion, but I don't think it matters much.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Leaving it as it is.

EmotiBitSerial::sendMessage(EmotiBitFactoryTest::TypeTag::FIRMWARE_VERSION, emotibitFwVersion);
// instructions
Serial.println("Options available:");
Serial.println("1. Add a wifi credential. Send: @WA,{\"ssid\":\"SSSS\",\"password\" : \"PPPP\"}~");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Come to think of it, we could probably use variables here instead of magic hard coded @ls~ etc, but it's not a huge deal since we're probably loathe to change these.

@nitin710 nitin710 merged commit b4b8417 into master Mar 26, 2024
@nitin710 nitin710 deleted the feat-sdCardWiFiCredentials branch March 26, 2024 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants