diff --git a/platform b/platform index 0723a1d..35a7eb5 160000 --- a/platform +++ b/platform @@ -1 +1 @@ -Subproject commit 0723a1dddf5c0c2669af7e356acea718f141092c +Subproject commit 35a7eb5b406feb8ff5d0352bc0bb8dc669bd5577 diff --git a/src/sword/mqttChannel.hpp b/src/sword/mqttChannel.hpp index 10d18c4..f1bee99 100644 --- a/src/sword/mqttChannel.hpp +++ b/src/sword/mqttChannel.hpp @@ -8,12 +8,12 @@ class MqttChannel { : Config(cfg), IoStat(ioStat), Logger(log), - runTopic(cfg.getStationPath() + "run"), - cfgTopic(cfg.getStationPath() + "cfg"), - verTopic(cfg.getStationPath() + "ver"), - eventTopic(cfg.getStationPath() + "event"), - statTopic(cfg.getStationPath() + "stat"), - resetTopic(cfg.getStationPath() + "reset"), + runTopic(cfg.StationPath + "run"), + cfgTopic(cfg.StationPath + "cfg"), + verTopic(cfg.StationPath + "ver"), + eventTopic(cfg.StationPath + "event"), + statTopic(cfg.StationPath + "stat"), + resetTopic(cfg.StationPath + "reset"), gameCommands({ {"gameStarted", GameModeCommands_e::GameStarted}, @@ -36,9 +36,9 @@ class MqttChannel { }), subTopics{{runTopic, cfgTopic, resetTopic}}, client("Sword", - cfg.getBrockerUrl(), + cfg.BrockerUrl, subTopics, - {cfg.getMqttUser(), cfg.getMqttPassword()}) { + {cfg.MqttUser, cfg.MqttPassword}) { fmt::println("MQTT: Starting task:"); mqttCall = [this](string topic, string payload) { if (topic == resetTopic || @@ -134,13 +134,13 @@ class MqttChannel { instance.IoStat.SwordLatch.moveOut(); } break; case ServiceCommands_e::PlaySoundPromo: { - MediaPlayer::playWavFile(instance.Config.getSoundPromo()); + MediaPlayer::playWavFile(instance.Config.SoundPromo); } break; case ServiceCommands_e::PlaySoundDeny: { - MediaPlayer::playWavFile(instance.Config.getSoundDeny()); + MediaPlayer::playWavFile(instance.Config.SoundDeny); } break; case ServiceCommands_e::PlaySoundChosen: { - MediaPlayer::playWavFile(instance.Config.getSoundAllow()); + MediaPlayer::playWavFile(instance.Config.SoundAllow); } break; } } diff --git a/src/sword/swordMain.cpp b/src/sword/swordMain.cpp index 65fead8..b2434fe 100644 --- a/src/sword/swordMain.cpp +++ b/src/sword/swordMain.cpp @@ -1,4 +1,5 @@ #include "swordMain.hpp" + #include "defines.hpp" #include "mqttChannel.hpp" @@ -11,7 +12,7 @@ constexpr uint8_t VersionArr[4] = {'V', 0, 8, 0}; const string Version = "V 0.8.0"; // const string Version = fmt::format("{} {}.{}.{}", VersionArr[0], VersionArr[1], VersionArr[2], VersionArr[3]); -SwordConfig SwordCfg{}; +unique_ptr SwordCfg; unique_ptr IoStat; OpMode Mode = OpMode::Maintanance; @@ -40,7 +41,7 @@ int main(int argc, char* argv[]) { CLI11_PARSE(app, argc, argv); SeqLogger log(logUrl, logApiKey, "Sword"); - SwordCfg.setVar(assetsBasePath, brocker, topic, user, password); + SwordCfg = make_unique(assetsBasePath, brocker, topic, user, password); fmt::println("{}", app.get_description()); if (modeArg == "promo") { @@ -79,7 +80,7 @@ int main(int argc, char* argv[]) { IoStat = make_unique(SwordEntry, SwordEnd, SwordPulled, SwordLatch); - MqttChannel mqtt(Version, SwordCfg, *IoStat, log); + MqttChannel mqtt(Version, *SwordCfg, *IoStat, log); auto resetCall = [&]() { log.sendEvent("Reset", ""); }; @@ -118,7 +119,8 @@ int main(int argc, char* argv[]) { } void processPromo(MqttChannel& mqtt, SeqLogger& log) { - auto tn = chrono::steady_clock::now(); + Tick tn(1s); + Tick tsf(30min); auto& serCmds = mqtt.getSerCommands(); while (true) { Thread::sleep(10ms); @@ -126,14 +128,19 @@ void processPromo(MqttChannel& mqtt, SeqLogger& log) { if (IoStat->SwordPulled.isPosEdge()) { if (!IoStat->Mute) { log.sendEvent("Pull", "Start Audio"); - MediaPlayer::playWavFile(SwordCfg.getSoundPromo()); + + if (tsf.tick()) { + MediaPlayer::playWavFile(SwordCfg->SoundPromo); + } else { + MediaPlayer::playWavFile(SwordCfg->SoundPromoShort); + } + fmt::println("Waiting for {}", IoStat->PromoDelay); Thread::sleep(IoStat->PromoDelay); fmt::println("Waiting done"); } } - if (chrono::steady_clock::now() > (tn + 1s)) { - tn = chrono::steady_clock::now(); + if (tn.tick()) { mqtt.sendStatusMessage(Mode); } } @@ -167,7 +174,7 @@ void processProduction(MqttChannel& mqtt, SeqLogger& log) { if (IoStat->SwordPulled.isPosEdge()) { mqtt.sendStatusMessage(Mode); // say: You are not the one - MediaPlayer::playWavFile(SwordCfg.getSoundDeny()); + MediaPlayer::playWavFile(SwordCfg->SoundDeny); Thread::sleep(15s); log.sendEvent("Req", "Pulled"); mqtt.sendEvent("pull"); @@ -196,7 +203,7 @@ void processProduction(MqttChannel& mqtt, SeqLogger& log) { mqtt.sendEvent("pulled-out"); log.sendEvent("Req", "Pulled Out"); // say: You ARE the one - MediaPlayer::playWavFile(SwordCfg.getSoundAllow()); + MediaPlayer::playWavFile(SwordCfg->SoundAllow); Thread::sleep(15s); mqtt.sendEvent("ceremonyDone"); diff --git a/src/sword/swordMain.hpp b/src/sword/swordMain.hpp index f98c946..988339d 100644 --- a/src/sword/swordMain.hpp +++ b/src/sword/swordMain.hpp @@ -28,7 +28,6 @@ #include "textUtils.hpp" #include "thread.hpp" - class LinMot { public: LinMot(OutputPort& o1, OutputPort& o2) @@ -79,45 +78,31 @@ class DeviceIoState { bool Mute{false}; }; -class SwordConfig { - public: - std::string getSoundPromo() { return soundPromo; } - std::string getSoundDeny() { return soundDeny; } - std::string getSoundAllow() { return soundAllow; } - std::string getBrockerUrl() { return brockerUrl; } - std::string getMqttUser() { return mqttUser; } - std::string getMqttPassword() { return mqttPassword; } - std::string getStationPath() { return stationPath; } - - void setVar(std::string base, +struct SwordConfig { + SwordConfig(std::string base, std::string brocker, std::string topic, - std::string user, std::string passwd) { - if (wasSet) { - return; - } - soundPromo = base + soundPromo; - soundDeny = base + soundDeny; - soundAllow = base + soundAllow; + std::string user, std::string passwd) + : AssetBase(base), + SoundPromo{base + "/promo.wav"}, + SoundPromoShort{base + "/promoShort.wav"}, + SoundDeny{base + "/deny.wav"}, + SoundAllow{base + "/allow.wav"}, + BrockerUrl(brocker), + StationPath(topic), + MqttUser(user), + MqttPassword(passwd) {} - brockerUrl = brocker; - stationPath = topic; - mqttUser = user; - mqttPassword = passwd; - wasSet = true; - } + const std::string AssetBase; - private: - bool wasSet{}; - std::string assetBase{}; - std::string soundPromo{"/promo.wav"}; - std::string soundPromoShort{"/promoShort.wav"}; - std::string soundDeny{"/deny.wav"}; - std::string soundAllow{"/allow.wav"}; + const std::string SoundPromo; + const std::string SoundPromoShort; + const std::string SoundDeny; + const std::string SoundAllow; - std::string brockerUrl; - std::string mqttUser; - std::string mqttPassword; - std::string stationPath; + const std::string BrockerUrl; + const std::string StationPath; + const std::string MqttUser; + const std::string MqttPassword; }; class MediaPlayer { diff --git a/src/throne/CMakeLists.txt b/src/throne/CMakeLists.txt new file mode 100644 index 0000000..9461b82 --- /dev/null +++ b/src/throne/CMakeLists.txt @@ -0,0 +1,13 @@ +add_executable(ThroneBridgeMain throneBridgeMain.cpp) + +target_link_libraries(ThroneBridgeMain + PkgConfig::LIBGPIOD + base + palAdapt + loRa + os + Mqtt + fmt::fmt + utils + Ilogging + ) diff --git a/src/throne/defines.hpp b/src/throne/defines.hpp new file mode 100644 index 0000000..d6cfa43 --- /dev/null +++ b/src/throne/defines.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include + +static constexpr uint16_t UndefCmd = 0xFFFF; + +enum IncommingCommands_e : uint16_t { + Qurey = 0x00, // Qurey info + SetMaintanance = 0x01, // Set Maintanance mode + SetPromoMode = 0x02, // Enable PromoMode + SetGamingMode = 0x03, // Enable in Game mode +}; + +enum ServiceCommands_e { + SetConfigVolume = 0x11, // Set Volume + SetConfigPromoDelay, // Set Delay between Promo + PlaySoundPromo, + PlaySoundCoronation, +}; + +enum GameModeCommands_e { + GameStarted = 0x21, // One Game has started (mute Promo mode) + GameFinished, // The Game is finished (resume to Promo mode) + GameOpenCoinSlot, // Lock sword on next try + GameCloseCoinSlot, // Release sword on next try +}; + +enum OutgoingCommands_e { + Status = 0x00, // Send Status + Response = 0x01, // Confirm last command + Error = 0x02, // Send error status + PullHappend = 0x03 // Message if User tried to pull the sword +}; + +struct StatusFrame { + uint8_t cmd{OutgoingCommands_e::Status}; + uint8_t version[4]; + uint8_t mode; + uint8_t healt; + uint8_t sensors; +}; + +struct ResponseFrame { + uint8_t cmd{OutgoingCommands_e::Response}; + uint8_t resCmd; + uint8_t status; +}; + +struct RxMsg { + RxMsg(uint16_t m) + : code(m) {} + uint16_t code; +}; + +struct TxMsg { + TxMsg(uint32_t m) + : cmd(m) {} + uint8_t cmd; +}; + +struct MqttQe { + MqttQe(std::string topic, std::string payload) + : Topic(topic), + Payload(payload) {} + std::string Topic; + std::string Payload; +}; + +struct ResponseTxMsg : public TxMsg { + ResponseTxMsg(uint16_t cmd, uint8_t stat) + : TxMsg(OutgoingCommands_e::Response) {}; + uint16_t resCmd; + uint8_t status; +}; +struct StatusTxMsg : public TxMsg { + StatusTxMsg(uint8_t mode, uint8_t swEnd, uint8_t swBeg, uint8_t swPull, uint8_t out) + : TxMsg(OutgoingCommands_e::Status), + _mode(mode), + _inSwEnd(swEnd), + _inSwBeg(swBeg), + _inSwPull(swPull), + _out(out) { + } + uint8_t _mode; + bool _inSwEnd; + bool _inSwBeg; + bool _inSwPull; + bool _out; +}; + +constexpr int GPIO_RST_PIN = 1; +constexpr int GPIO_DIO0_PIN = 27; diff --git a/src/throne/throneMain.cpp b/src/throne/throneMain.cpp new file mode 100644 index 0000000..ebce1bd --- /dev/null +++ b/src/throne/throneMain.cpp @@ -0,0 +1,404 @@ +#include "throneBridgeMain.hpp" + +using namespace pal; +using namespace std::literals::string_view_literals; +using namespace std::literals::chrono_literals; +using namespace std; + +constexpr uint8_t VersionArr[4] = {'V', 0, 8, 0}; +const string Version = "V 0.8.0"; +// const string Version = fmt::format("{} {}.{}.{}", VersionArr[0], VersionArr[1], VersionArr[2], VersionArr[3]); + +SwordConfig SwordCfg{}; +unique_ptr IoStat; +OpMode Mode = OpMode::Maintanance; + +void processMaintanence(MqttChannel& mqtt); +void processPromo(MqttChannel& mqtt, SeqLogger& log); +void processProduction(MqttChannel& mqtt, SeqLogger& log); + +/// @brief MQTT-Service provider +void MQttServiceTask(Thread::Context& ctx, Queue& rxQueue, Queue& txQueue) { + string runTopic = SwordCfg.getStationPath() + "run"; + string intTopic = SwordCfg.getStationPath() + "int"; + string statTopic = SwordCfg.getStationPath() + "stat"; + + fmt::println("MQTT: Starting task:"); + fmt::println(" Topic: {}", runTopic); + + auto mqttCallback = [&](string topic, string payload) { + fmt::print("MQTT: on sendTrigger {}:{}'\n", topic, payload); + + static std::map commands = { + {"Qurey", IncommingCommands_e::Qurey}, + {"SetMaintanance", IncommingCommands_e::SetMaintanance}, + {"SetPromoMode", IncommingCommands_e::SetPromoMode}, + {"SetGamingMode", IncommingCommands_e::SetGamingMode}, + }; + static std::map serCommands = { + {"ServiceOpen", ServiceCommands_e::Open}, + {"ServiceClose", ServiceCommands_e::Close}, + {"ServicePlay1", ServiceCommands_e::Play1}, + {"ServicePlay2", ServiceCommands_e::Play2}, + {"ServicePlay3", ServiceCommands_e::Play3}, + }; + static std::map gameCommands = { + {"GameStayLocked", GameModeCommands_e::GameNextTryLocked}, + {"GameReleaseLock", GameModeCommands_e::GameNextTryRelease}, + }; + + if (topic != runTopic) { + fmt::print("MQTT: ignore invalid topic {}'\n", topic); + return; + } + + if (commands.contains(payload)) { + rxQueue.emplace(commands.at(payload)); + return; + } + if (serCommands.contains(payload)) { + auto code = serCommands.at(payload); + rxQueue.emplace(code); + return; + } + if (gameCommands.contains(payload)) { + rxQueue.emplace(serCommands.at(payload)); + return; + } + + fmt::print("MQTT: unknown command {}:{}'\n", topic, payload); + }; + + try { + std::vector topicsToRegister{runTopic}; + MqttClient client("Sword", SwordCfg.getBrockerUrl(), topicsToRegister, {SwordCfg.getMqttUser(), SwordCfg.getMqttPassword()}); + fmt::println("MQTT: service: successfully connected ..."); + client.registerCallback(mqttCallback); + + client.send(statTopic, fmt::format("{} Mode={}, sE:0,sS:0,sP:0,lE:0", + Version, (int)Mode)); + + int cnt = 0; + fmt::println("MQTT: Running ..."); + while (!ctx.isCancelled()) { + auto tx = txQueue.dequeue(); + + switch (tx->cmd) { + case OutgoingCommands_e::Status: { + auto& s = *IoStat; + client.send(statTopic, fmt::format("{} Mode={}, sE:{},sS:{},sP:{},lE:{}", + Version, (int)Mode, + s.SwordEnd.isActive(), + s.SwordEntry.isActive(), + s.SwordPulled.isActive(), + s.SwordLatch.isLocked())); + } break; + case OutgoingCommands_e::Response: + client.send(intTopic, "some response"); + break; + case OutgoingCommands_e::Error: + client.send(intTopic, "some error"); + break; + case OutgoingCommands_e::PullHappend: + client.send(intTopic, "pulled"); + break; + + default: + break; + } + client.send(intTopic, "sent"); + } + } catch (const exception& exc) { + fmt::println(stderr, "\nERROR: Unable to connect to Setup bridge: {}", exc.what()); + ctx.markFailed(); + } +} + +/// @brief LoRa-Service provider +void LoRaServiceTask(Thread::Context& ctx, Queue& rxQueue, Queue& txQueue) { + fmt::println("LoRa: Starting task:"); + + auto loraCallback = [&](std::vector data) { + auto hex = bytesToHex(data); + auto str = bytesToPrintable(data); + fmt::print("LoRa: Received Byts: {}\n 0x{}\n {}\n", data.size(), hex, str); + + auto e = std::make_unique(0); + rxQueue.enqueue(std::move(e)); + return true; + }; + auto spi = SpiPort({.mode = SpiPort::Mode0, + .cs = SpiPort::Cs0, + .frequ = 1'000'000}); + + LoRa loRa({ + .frequency = 868'000'000, + .port = spi, + .DioPin = GPIO_DIO0_PIN, + .resetPin = GPIO_RST_PIN, + }, + loraCallback); + + fmt::println("LoRa: Running ..."); + while (!ctx.isCancelled()) { + auto tx = txQueue.dequeue(); + loRa.send({0}); + // printf ("Message: %s %lu\n", msg->message.c_str(), msg->cnt); + } + + try { + } catch (const exception& exc) { + fmt::println(stderr, "\nERROR: Unable to connect to Setup bridge: {}", exc.what()); + ctx.markFailed(); + } +} + + + +int main(int argc, char* argv[]) { + CLI::App app{"Sword Firmware"}; + string modeArg = "test"; + string assetsBasePath = "/home/user/assets"; + string brocker = "mqtts://nerdyssey.de:8883"; + string topic = "staging/outdoor/king-arthur/sword/"; + string user = "exodususer"; + string password = "awesomeexodusoutdooradventures"; + string logUrl = "log.nerdyssey.de"; + string logApiKey = "uYpJC6WyZAbFK2MUgwNH"; + app.add_option("startmode", modeArg, "Startup mode")->required(); + app.add_option("basepath", assetsBasePath, "Base bath for assets")->required(); + app.add_option("-b,--brocker", brocker, "MQTT Topic URL"); + app.add_option("-t,--topic", topic, "MQTT Topic"); + app.add_option("-p,--passwd", password, "MQTT Brocker password"); + app.add_option("-u,--user", user, "MQTT Brocker user"); + app.add_option("-l,--log", logUrl, "Logging Service"); + app.add_option("-k,--key", logApiKey, "Logging API Key"); + + CLI11_PARSE(app, argc, argv); + + SeqLogger log(logUrl, logApiKey, "Sword"); + SwordCfg.setVar(assetsBasePath, brocker, topic, user, password); + + fmt::println("{}", app.get_description()); + if (modeArg == "promo") { + fmt::println("starting with promo"); + Mode = OpMode::PromoMode; + } else if (modeArg == "game") { + fmt::println("starting with promo"); + Mode = OpMode::GamingMode; + } else { + fmt::println("unknown {}", modeArg); + } + constexpr array modes = {"Undefined", "Maintanance", "PromoMode", "Production"}; + log.sendBootUp(Version, modes[static_cast(Mode)]); + + auto itrHandler = [&]() { + TriggeredInput itr(26, "PItr"); + while (true) { + cout << "Await next interrupt\n"; + if (itr.await()) { + cout << "Interrupt Event\n"; + } + } + }; + thread interruptThread(itrHandler); + + InputPort sbPort(SwordEntryPin, "PinSEntry"); + InputPort sePort(SwordEndPin, "PInSEnd"); + InputPort spPort(SwordPulledPin, "PInSPull"); + InputPort sparePort(sparePin, "spare"); + OutputPort mOut1(LinMotPin1, "M1:1"); + OutputPort mOut2(LinMotPin2, "M1:2"); + InputTransAware SwordEntry(sbPort); + InputTransAware SwordEnd(sePort); + InputTransAware SwordPulled(spPort); + LinMot SwordLatch(mOut1, mOut2); + + IoStat = make_unique(SwordEntry, SwordEnd, SwordPulled, SwordLatch); + + static Queue rxQueue; + static Queue txQueue; + MqttChannel mqtt(Version, SwordCfg, *IoStat, log); + auto resetCall = [&]() { + log.sendEvent("Reset", ""); + }; + + mqtt.flushInput(); + static auto conTask = Thread::createExplicit("MqttTask", MqttChannel::runMasterService, ref(mqtt), resetCall); + + SwordLatch.moveOut(); + fmt::println("Running App"); + while (true) { + switch (Mode) { + case OpMode::Maintanance: { + fmt::println("Running Maintanance"); + // txQueue.emplace(msg->code, 0); + processMaintanence(mqtt); + // auto e = make_unique(msg->code, 0); + // txQueue.enqueue(move(e)); + } break; + case OpMode::PromoMode: { + fmt::println("Running PromoMode"); + processPromo(mqtt, log); + } break; + case OpMode::GamingMode: { + fmt::println("Running GamingMode"); + processProduction(mqtt, log); + } break; + + default: + break; + } + } + interruptThread.join(); + return EXIT_SUCCESS; +} + +void processMaintanenceCommand(enum ServiceCommands_e code) { + switch (code) { + case ServiceCommands_e::SetLockOpen: { + IoStat->SwordLatch.moveIn(); + } break; + case ServiceCommands_e::SetLockClose: { + IoStat->SwordLatch.moveOut(); + } break; + case ServiceCommands_e::PlaySoundPromo: { + MediaPlayer::playWavFile(SwordCfg.getSoundPromo()); + } break; + case ServiceCommands_e::PlaySoundDeny: { + MediaPlayer::playWavFile(SwordCfg.getSoundDeny()); + } break; + case ServiceCommands_e::PlaySoundChosen: { + MediaPlayer::playWavFile(SwordCfg.getSoundAllow()); + } break; + } +} + +void processMaintanence(MqttChannel& mqtt) { + auto& serCmds = mqtt.getSerCommands(); + while (true) { + auto msg = mqtt.tryDequeueFor(500ms); + if (msg) { + if (serCmds.contains(msg->Payload)) { + auto code = serCmds.at(msg->Payload); + processMaintanenceCommand(code); + } + } + IoStat->tick(); + mqtt.sendStatusMessage(Mode); + } +} + +void processPromo(MqttChannel& mqtt, SeqLogger& log) { + auto tn = chrono::steady_clock::now(); + auto& serCmds = mqtt.getSerCommands(); + while (true) { + auto msg = mqtt.tryDequeueFor(10ms); + if (msg) { + if (serCmds.contains(msg->Payload)) { + auto code = serCmds.at(msg->Payload); + processMaintanenceCommand(code); + } + } + IoStat->tick(); + if (IoStat->SwordPulled.isPosEdge()) { + log.sendEvent("Pull", "Start Audio"); + MediaPlayer::playWavFile(SwordCfg.getSoundPromo()); + fmt::println("Waiting for {}", IoStat->PromoDelay); + Thread::sleep(IoStat->PromoDelay); + fmt::println("Waiting done"); + } + if (chrono::steady_clock::now() > (tn + 1s)) { + tn = chrono::steady_clock::now(); + mqtt.sendStatusMessage(Mode); + } + } +} + +void processProduction(MqttChannel& mqtt, SeqLogger& log) { + auto& serCmds = mqtt.getSerCommands(); + auto& gameCmds = mqtt.getGameCommands(); + while (true) { + auto msg = mqtt.tryDeque(); + if (!msg) { + continue; + } + + if (serCmds.contains(msg->Payload)) { + auto code = serCmds.at(msg->Payload); + processMaintanenceCommand(code); + continue; + } + + auto code = gameCmds.at(msg->Payload); + switch (code) { + case GameModeCommands_e::GameStarted: { + fmt::println("Got GameStarted"); + log.sendEvent("Req", "GameStarted"); + } + case GameModeCommands_e::GameFinished: { + fmt::println("Got GameFinished"); + log.sendEvent("Req", "GameFinished"); + } + + case GameModeCommands_e::GameNextTryLocked: { + fmt::println("Got NextTryLock"); + log.sendEvent("Req", "StayLockded"); + while (true) { + IoStat->SwordPulled.sample(); + if (IoStat->SwordPulled.isPosEdge()) { + mqtt.sendStatusMessage(Mode); + // say: You are not the one + MediaPlayer::playWavFile(SwordCfg.getSoundDeny()); + Thread::sleep(15s); + log.sendEvent("Req", "Pulled"); + mqtt.sendEvent("pull"); + } + } + } break; + + case GameModeCommands_e::GameNextTryRelease: { + fmt::println("NextTryRelease"); + log.sendEvent("Req", "UnlockSword"); + IoStat->SwordLatch.moveIn(); + while (true) { + IoStat->SwordEnd.sample(); + if (IoStat->SwordEnd.isNegEdge()) { + mqtt.sendStatusMessage(Mode); + break; + } + } + while (true) { + IoStat->SwordEntry.sample(); + if (IoStat->SwordEntry.isNegEdge()) { + mqtt.sendStatusMessage(Mode); + break; + } + } + mqtt.sendEvent("pulled-out"); + log.sendEvent("Req", "Pulled Out"); + // say: You ARE the one + MediaPlayer::playWavFile(SwordCfg.getSoundAllow()); + Thread::sleep(15s); + mqtt.sendEvent("ceremonyDone"); + + while (true) { + IoStat->SwordEnd.sample(); + if (IoStat->SwordEnd.isNegEdge()) { + mqtt.sendStatusMessage(Mode); + break; + } + } + while (true) { + IoStat->SwordEntry.sample(); + if (IoStat->SwordEntry.isNegEdge()) { + mqtt.sendStatusMessage(Mode); + break; + } + } + log.sendEvent("Req", "Put Back"); + mqtt.sendEvent("put-in"); + } break; + } + } +} diff --git a/src/throne/throneMain.hpp b/src/throne/throneMain.hpp new file mode 100644 index 0000000..09da544 --- /dev/null +++ b/src/throne/throneMain.hpp @@ -0,0 +1,97 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MqttClient.hpp" +#include "coreExcept.hpp" +#include "gpioPort.hpp" +#include "loRa.hpp" +#include "logging.hpp" +#include "mutex.hpp" +#include "palHw.hpp" +#include "queue.hpp" +#include "semaphore.hpp" +#include "spiPort.hpp" +#include "textUtils.hpp" +#include "thread.hpp" + +// internal interfaces +#include "defines.hpp" + +class DeviceIoState { + public: + DeviceIoState(int& count, + bool& protect) + : CoinAccepted(count), + CoinDenied(count), + CoinProtector(protect) { + } + + int& CoinAccepted; + int& CoinDenied; + bool& CoinProtector; + + std::chrono::seconds PromoDelay{60}; + int Volume{100}; +}; + +class ThroneConfig { + public: + std::string getSoundPromo() { return soundPromo; } + std::string getSoundCoronation() { return soundCoronation; } + std::string getBrockerUrl() { return brockerUrl; } + std::string getMqttUser() { return mqttUser; } + std::string getMqttPassword() { return mqttPassword; } + std::string getStationPath() { return stationPath; } + + void setVar(std::string base, + std::string brocker, std::string topic, + std::string user, std::string passwd) { + if (wasSet) { + return; + } + soundPromo = base + soundPromo; + soundCoronation = base + soundCoronation; + + brockerUrl = brocker; + stationPath = topic; + mqttUser = user; + mqttPassword = passwd; + wasSet = true; + } + + private: + bool wasSet{}; + std::string soundPromo{"/promo.wav"}; + std::string soundCoronation{"/coronation.wav"}; + + std::string brockerUrl; + std::string mqttUser; + std::string mqttPassword; + std::string stationPath; +}; + +constexpr int CoinProtectPin = 16; +constexpr int CoinCntPin = 6; +constexpr int GPIO_RST_PIN = 1; +constexpr int GPIO_DIO0_PIN = 27; +