diff --git a/platformio.ini b/platformio.ini index c2e0927..b5588ca 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,11 +13,10 @@ platform = atmelsam board = adafruit_feather_m0 framework = arduino monitor_speed = 115200 -monitor_flags = - --raw - --echo +monitor_raw = yes +monitor_echo = yes extra_scripts = ./upload_script.py -test_build_project_src = true +test_build_src = true lib_deps = ArduinoJson@~6.17.2 StreamUtils@~1.6.0 diff --git a/src/Application/App-serial.cpp b/src/Application/App-serial.cpp index 9adedd8..aff723c 100644 --- a/src/Application/App-serial.cpp +++ b/src/Application/App-serial.cpp @@ -1,38 +1,117 @@ +#ifdef COMPONENT_TEST #include #include namespace { - struct SerialRequest { - const char * cmd; - const JsonArray & path; - const JsonDocument & input; - }; - using FunctionType = std::function; - using StringToFunc = std::unordered_map; + using func = std::function args)>; + using StringToFunc = std::unordered_map; StringToFunc mapNameToCallback; constexpr const char EOT = '\4'; void endTransmission() { - print(EOT); + println(EOT); + } + + // https://stackoverflow.com/questions/4654636/how-to-determine-if-a-string-is-a-number-with-c + bool is_number(const std::string& s) + { + std::string::const_iterator it = s.begin(); + while (it != s.end() && std::isdigit(*it)) ++it; + return !s.empty() && it == s.end(); + } + + int string_to_tpic(const std::string& s){ + if(s == "air"){ + return TPICDevices::AIR_VALVE; + } + if(s == "alcohol"){ + return TPICDevices::ALCHOHOL_VALVE; + } + if(s == "flush"){ + return TPICDevices::FLUSH_VALVE; + } + return -1; + } + + void ListCommands(){ + println("Available serial commands: "); + println("open [list of ints] - open a list of filter valves"); + println("open [air/alcohol/flush] - open said valve"); + println("close [list of ints] - close a list of filter valves"); + println("close [air/alcohol/flush] - close said valve"); + println("pump [on/off/reverse] - change state of pump"); + println("intake [on/off] - change state of intake valve"); + println("query [status/config/ram/time/sensors] - print info about these objects"); + println("alarm [int] - schedule alarm interrupt n seconds in the future (will print info but do nothing)"); + return; } } // namespace // Registering Top-level serial commands void App::setupSerialRouting() { - mapNameToCallback["hyperflush"] = [this](SerialRequest req) { - const auto response = dispatchAPI(); - serializeJson(response, Serial); + mapNameToCallback["help"] = [this](std::vector args) { + ListCommands(); + return; + }; + + mapNameToCallback["open"] = [this](std::vector args) { + //assume that it is a list of valveIDs + if(is_number(args[1])){ + for(unsigned int i = 1; i < args.size(); i++){ + shift.setPin( std::stoi(args[i]) + shift.capacityPerRegister, HIGH); // adding capacityPerRegister skips the first register + } + } else { + if(string_to_tpic(args[1]) != -1){ + shift.setPin(string_to_tpic(args[1]), HIGH); + } + } + shift.write(); + return; + }; + + mapNameToCallback["close"] = [this](std::vector args) { + if(is_number(args[1])){ + for(unsigned int i = 1; i < args.size(); i++){ + shift.setPin( std::stoi(args[i]) + shift.capacityPerRegister, LOW); + } + } else { + if(string_to_tpic(args[1]) != -1){ + shift.setPin(string_to_tpic(args[1]), LOW); + } + } + shift.write(); + return; + }; + + mapNameToCallback["pump"] = [this](std::vector args) { + const char * endpoint = args[1].c_str(); + if (strcmp(endpoint, "on") == 0) { + pump.on(); + } + if (strcmp(endpoint, "reverse") == 0) { + pump.on(Direction::reverse); + } + if (strcmp(endpoint, "off") == 0) { + pump.off(); + } + return; }; - mapNameToCallback["debubble"] = [this](SerialRequest req) { - const auto response = dispatchAPI(); - serializeJson(response, Serial); + mapNameToCallback["intake"] = [this](std::vector args){ + const char * endpoint = args[1].c_str(); + if(strcmp(endpoint, "on") == 0){ + intake.on(); + } + if(strcmp(endpoint, "off") == 0){ + intake.off(); + } + return; }; - mapNameToCallback["query"] = [this](SerialRequest req) { - const char * endpoint = req.path[1]; + mapNameToCallback["query"] = [this](std::vector args) { + const char * endpoint = args[1].c_str(); if (strcmp(endpoint, "status") == 0) { const auto & response = dispatchAPI(); serializeJson(response, Serial); @@ -52,10 +131,43 @@ void App::setupSerialRouting() { endTransmission(); return; } + + if(strcmp(endpoint, "time") == 0) { + power.printCurrentTime(); + return; + } + + if (strcmp(endpoint, "sensors") == 0) { + if(sensors.pressure.enabled){ + println("Pressure sensor detected"); + } else { + println(RED("Pressure sensor not detected")); + } + if(sensors.baro1.enabled){ + println("Baro1 sensor detected"); + }else{ + println(RED("Baro1 sensor not detected")); + } + if(sensors.baro2.enabled){ + println("Baro2 sensor detected"); + }else{ + println(RED("Baro2 sensor not detected")); + } + return; + } + }; + + mapNameToCallback["alarm"] = [this](std::vector args) { + long time = std::labs(std::stol(args[1])); + power.scheduleNextAlarm(time + now()); + return; }; - mapNameToCallback["reset"] = [this](SerialRequest req) { - const char * endpoint = req.path[1]; + // This function is pointless in component testing mode because you can't sample in the mode + // Keeping the code commented out in case this changes in the future + +/* mapNameToCallback["reset"] = [this](std::vector args) { + const char * endpoint = args[1].c_str(); if (strcmp(endpoint, "valves") == 0) { for (int i = 0; i < config.numberOfValves; i++) { vm.setValveStatus(i, ValveStatus::Code(config.valves[i])); @@ -69,83 +181,29 @@ void App::setupSerialRouting() { endTransmission(); return; } - }; + }; */ } -/** - * - * { - * path: ["top-level command", "sub1", "sub2"], - * cmd: "" - * ... - * } - */ - void App::commandReceived(const char * msg, size_t size) { - if (msg[0] == '{') { - StaticJsonDocument<512> input; - deserializeJson(input, msg); - - for (auto & item : mapNameToCallback) { - if (item.first == input["cmd"].as()) { - item.second({input["cmd"], input["path"], input}); - break; - } - } - } - - // if (strcmp(msg, "flow temp")) { - // // auto response = FlowSensor::read(); - // } - - // if (strcmp(msg, "flow flow")) {} - // KPString line{msg}; - - // println(line); - - // if (msg[0] == '{') { - // StaticJsonDocument<255> doc; - // deserializeJson(doc, msg); - - // StaticJsonDocument<255> response; - // // dispatch(doc["cmd"].as(), doc, response); - // } - if (strcmp(msg, "status")) { - println(status); + std::vector args; + char str[80]; + strcpy(str, msg); + println(""); + const char delim[2] = " "; + const char * tok = strtok(str, delim); + while (tok != NULL) { + args.push_back(tok); + tok = strtok(NULL, delim); } - - // if (line == "print config") { - // println(config); - // } - - // if (line == "schedule now") { - // println("Schduling temp task"); - // Task task = tm.createTask(); - // task.schedule = now() + 5; - // task.flushTime = 5; - // task.sampleTime = 5; - // task.deleteOnCompletion = true; - // task.status = TaskStatus::active; - // task.valves.push_back(0); - // tm.insertTask(task); - // println(scheduleNextActiveTask().description()); - // } - - if (strcmp(msg, "reset valves") == 0) { - for (int i = 0; i < config.numberOfValves; i++) { - vm.setValveStatus(i, ValveStatus::Code(config.valves[i])); + for(auto & command : mapNameToCallback) { + if(command.first == args[0]){ + command.second(args); + return; } } - - // vm.writeToDirectory(); - // } - - // if (line == "mem") { - // println(free_ram()); - // } - - // if (line == "hyperflush") { - // beginHyperFlush(); - // }; -} \ No newline at end of file + //if input doesn't match any commands + ListCommands(); + return; +} +#endif \ No newline at end of file diff --git a/src/Application/App.hpp b/src/Application/App.hpp index 94ff41c..a633d94 100644 --- a/src/Application/App.hpp +++ b/src/Application/App.hpp @@ -123,10 +123,10 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv // // ─── ADDING COMPONENTS ─────────────────────────────────────────── // - +#ifdef COMPONENT_TEST addComponent(KPSerialInput::sharedInstance()); setupSerialRouting(); - +#endif addComponent(ActionScheduler::sharedInstance()); addComponent(fileLoader); addComponent(shift); @@ -223,6 +223,9 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv file.close(); } + + +#ifndef COMPONENT_TEST // RTC Interrupt callback power.onInterrupt([this]() { println(GREEN("RTC Interrupted!")); @@ -230,22 +233,32 @@ class App : public KPController, public KPSerialInputObserver, public TaskObserv interrupts(); }); - - nowSampleButton.onInterrupt([this](){ + nowSampleButton.onInterrupt([this](){ if(!power.rtc.alarm(1) && !power.rtc.alarm(2)){ println(GREEN("Sample Now Button Interrupted!")); println(beginNowTask().description()); } interrupts(); }); +#else + // RTC Interrupt callback + power.onInterrupt([this]() { + println(GREEN("RTC Interrupted!")); + interrupts(); + }); + + nowSampleButton.onInterrupt([this](){ + if(!power.rtc.alarm(1) && !power.rtc.alarm(2)){ + println(GREEN("Sample Now Button Interrupted!")); + } + interrupts(); + }); +#endif + nowSampleButton.setSampleButton(); runForever(1000, "detailLog", [&]() { logDetail("detail.csv"); }); -#if defined(DEBUG) || defined(COMPONENT_TEST) +#if defined(DEBUG) runForever(2000, "memLog", [&]() { printFreeRam(); }); -#endif - -#ifndef COMPONENT_TEST - nowSampleButton.setSampleButton(); -#endif +#endif } void logDetail(const char * filename) {