diff --git a/lib/Domain/Gun/Atmega328pHal.hpp b/lib/Domain/Gun/Atmega328pHal.hpp
new file mode 100644
index 0000000..c016ee7
--- /dev/null
+++ b/lib/Domain/Gun/Atmega328pHal.hpp
@@ -0,0 +1,86 @@
+/*
+ *
+ * Copyright (c) 2023 Aurélien Labrosse
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+
+#include
+#include
+
+#define BSP_TICKS_PER_SEC 100
+#define F_CPU 16000000L
+
+#define LASER_PIN 10
+#define VIBRATOR_PIN 6
+#define BATTERY_VOLTAGE_PIN A3
+#define CHARGING_STATE_PIN A2
+
+#define MIN_BAT_VOLTAGE 3000
+#define MAX_BAT_VOLTAGE 4120
+
+ISR(TIMER2_COMPA_vect) {}
+
+class Atmega328pHal : public IGunHal {
+public:
+ ~Atmega328pHal() {}
+
+ Atmega328pHal() {
+
+ pinMode(VIBRATOR_PIN, OUTPUT);
+ pinMode(LASER_PIN, OUTPUT);
+ }
+ /*
+ * the 'loop' method shall be called each 10ms
+ *
+ * Configure timer2 so that an interrupt occurs each 10ms
+ * and wake up the MCU if it is in sleep mode.
+ */
+ void setupHeartbeat() override {
+ TCCR2A = (1U << WGM21) | (0U << WGM20); // set Timer2 in CTC mode
+ TCCR2B = (1U << CS22) | (1U << CS21) | (1U << CS20); // 1/1024 prescaler
+ ASSR &= ~(1U << AS2);
+ TIMSK2 = (1U << OCIE2A); // enable TIMER2 compare Interrupt
+ TCNT2 = 0U;
+
+ // set the output-compare register based on the desired tick frequency
+ OCR2A = (F_CPU / BSP_TICKS_PER_SEC / 1024U) - 1U;
+ }
+
+ void laserOn() override { PORTB |= (1 << PB2); }
+ void laserOff() override { PORTB &= ~(1 << PB2); }
+ void vibrationOn() override { PORTD |= (1 << PD6); }
+ void vibrationOff() override { PORTD &= ~(1 << PD6); }
+
+ uint16_t getBatteryVoltageMv() override {
+
+ analogRead(BATTERY_VOLTAGE_PIN);
+ float rawBatt = analogRead(BATTERY_VOLTAGE_PIN);
+ rawBatt += analogRead(BATTERY_VOLTAGE_PIN);
+ rawBatt += analogRead(BATTERY_VOLTAGE_PIN);
+ rawBatt += analogRead(BATTERY_VOLTAGE_PIN);
+ rawBatt /= 4;
+ uint16_t battMv = (5000 / 1023.f) * rawBatt;
+
+ return battMv;
+ };
+
+ uint8_t getBatteryVoltagePercent() override {
+ return map(getBatteryVoltageMv(), MIN_BAT_VOLTAGE, MAX_BAT_VOLTAGE, 0, 100);
+ }
+
+ virtual bool isCharging() override {
+ return (digitalRead(CHARGING_STATE_PIN) == HIGH);
+ }
+};
\ No newline at end of file
diff --git a/lib/Domain/Gun/Button.cpp b/lib/Domain/Gun/Button.cpp
new file mode 100644
index 0000000..93ceeba
--- /dev/null
+++ b/lib/Domain/Gun/Button.cpp
@@ -0,0 +1,18 @@
+/*
+ *
+ * Copyright (c) 2023 Aurélien Labrosse
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
diff --git a/lib/Domain/Gun.cpp b/lib/Domain/Gun/Button.hpp
similarity index 58%
rename from lib/Domain/Gun.cpp
rename to lib/Domain/Gun/Button.hpp
index 40f5832..527c2c3 100644
--- a/lib/Domain/Gun.cpp
+++ b/lib/Domain/Gun/Button.hpp
@@ -1,39 +1,28 @@
-/*
- *
- * Copyright (c) 2023 Aurélien Labrosse
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-#include
-
-static const uint8_t SHOTS_PER_REARM = 5;
-
-void Gun::onButton1ShortPress() {
- if (availableShots > 0) {
- _hal.laserOn();
- _hal.vibrationOn();
- _hal.shortDelay();
- _hal.laserOff();
- _hal.vibrationOff();
- availableShots--;
- }
-
-}
-
-void Gun::onButton1LongPress() {
- _hal.deepSleep();
-}
-void Gun::onButton2ShortPress() { availableShots = SHOTS_PER_REARM; }
-void Gun::onButton2LongPress() {
- _hal.laserOn();
-}
+/*
+ *
+ * Copyright (c) 2023 Aurélien Labrosse
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+class Button : public Contactor {
+
+public:
+ Button() : Contactor(), calibrationMode(false) {}
+
+ bool calibrationMode;
+ void onShortPress() override { shootCount = 0; }
+ void onLongPress() override { calibrationMode = !calibrationMode; }
+};
\ No newline at end of file
diff --git a/lib/Domain/Gun/Gun.cpp b/lib/Domain/Gun/Gun.cpp
new file mode 100644
index 0000000..539bfed
--- /dev/null
+++ b/lib/Domain/Gun/Gun.cpp
@@ -0,0 +1,143 @@
+/*
+ *
+ * Copyright (c) 2023 Aurélien Labrosse
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include
+#include
+
+volatile uint16_t hitcount;
+uint16_t previoushitcout;
+volatile uint16_t shootCount;
+
+#define TICKS_BETWEEN_UI_UPDATE 10
+
+// display API
+static void displayBatteryStatus();
+static void displayShootCount();
+
+void setup();
+static void setupHeartbeat();
+
+// counter for ticks between battery display update
+uint16_t updateBatteryDisplayCycleCount;
+
+Trigger trigger;
+Button button;
+
+volatile Contactor::Event pendingTriggerEvent;
+void triggerInterruptHandler() {
+ if (bit_is_set(PIND, PD2)) {
+ pendingTriggerEvent = Contactor::Event::Released;
+ } else {
+ pendingTriggerEvent = Contactor::Event::Pressed;
+ }
+}
+
+volatile Contactor::Event pendingButtonEvent;
+void buttonInterruptHandler() {
+ if (bit_is_set(PIND, PD3)) {
+ pendingButtonEvent = Contactor::Event::Released;
+ } else {
+ pendingButtonEvent = Contactor::Event::Pressed;
+ }
+}
+
+long now = 0;
+
+void Gun::loop(void) {
+
+ now += 10; // 10ms per loop thanks to timer2
+
+ trigger.decreaseCycleCount();
+
+ cli();
+ trigger.pendingEvent = pendingTriggerEvent;
+ button.pendingEvent = pendingButtonEvent;
+ pendingTriggerEvent = Contactor::Event::NoEvent;
+ pendingButtonEvent = Contactor::Event::NoEvent;
+ sei();
+
+ trigger.processPendingEvent(now);
+ button.processPendingEvent(now);
+
+ button.checkForLongPress(now);
+
+ if (button.calibrationMode) {
+ hal->laserOn();
+ hal->vibrationOff();
+
+ } else {
+ if (trigger.shootCycleCountdown == 0) {
+ hal->laserOff();
+ hal->vibrationOff();
+ ui->displayShootCount(shootCount);
+ }
+
+ if (updateBatteryDisplayCycleCount > 0) {
+ updateBatteryDisplayCycleCount--;
+ }
+ if (updateBatteryDisplayCycleCount == 0) {
+ displayBatteryStatus();
+ updateBatteryDisplayCycleCount = TICKS_BETWEEN_UI_UPDATE;
+ }
+ }
+
+ LowPower.idle(SLEEP_15MS, ADC_OFF, TIMER2_ON, TIMER1_ON, TIMER0_ON, SPI_OFF,
+ USART0_OFF, TWI_OFF);
+}
+
+
+void Gun::setup(void) {
+
+#if defined(SERIAL_OUTPUT)
+ Serial.begin(115200);
+ while (!Serial) {
+ }
+
+ shootCount = 0;
+ updateBatteryDisplayCycleCount = TICKS_BETWEEN_UI_UPDATE;
+
+ pinMode(LASER_PIN, OUTPUT);
+ digitalWrite(LASER_PIN, LOW);
+
+ pinMode(OUT2_PIN, OUTPUT);
+ digitalWrite(OUT2_PIN, LOW);
+
+ pinMode(BATTERY_VOLTAGE_PIN, INPUT);
+ pinMode(CHARGING_STATE_PIN, INPUT);
+
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+
+#if defined(SERIAL_OUTPUT)
+ Serial.println("Ready!");
+#endif
+
+ ui->displaySplash();
+ ui->displayBatteryStatus();
+ ui->displayShootCount();
+
+ pinMode(BUTTON2_PIN, INPUT_PULLUP);
+ attachInterrupt(digitalPinToInterrupt(BUTTON2_PIN), buttonInterruptHandler,
+ CHANGE);
+
+ pinMode(TRIGGER_PIN, INPUT_PULLUP);
+ attachInterrupt(digitalPinToInterrupt(TRIGGER_PIN), triggerInterruptHandler,
+ CHANGE);
+ cli();
+ setupHeartbeat();
+ sei();
+}
\ No newline at end of file
diff --git a/lib/Domain/Gun.hpp b/lib/Domain/Gun/Gun.hpp
similarity index 73%
rename from lib/Domain/Gun.hpp
rename to lib/Domain/Gun/Gun.hpp
index 4858b0b..cd6fb5f 100644
--- a/lib/Domain/Gun.hpp
+++ b/lib/Domain/Gun/Gun.hpp
@@ -16,18 +16,17 @@
*/
#pragma once
-#include
-#include
+#include
+#include
class Gun {
- IGunHal &_hal;
+ IGunHal *hal;
+ IGunUi *ui;
public:
- uint8_t availableShots;
- Gun(IGunHal &hal) : _hal(hal) {}
- void onButton1ShortPress();
- void onButton1LongPress();
- void onButton2ShortPress();
- void onButton2LongPress();
+ Gun(IGunHal *hal, IGunUi *ui) : hal(hal), ui(ui) {}
+
+ void loop();
+ void setup();
};
\ No newline at end of file
diff --git a/lib/Domain/IGunHal.hpp b/lib/Domain/Gun/IGunHal.hpp
similarity index 78%
rename from lib/Domain/IGunHal.hpp
rename to lib/Domain/Gun/IGunHal.hpp
index 4891b53..e050679 100644
--- a/lib/Domain/IGunHal.hpp
+++ b/lib/Domain/Gun/IGunHal.hpp
@@ -16,18 +16,22 @@
*/
#pragma once
+#include
+
class IGunHal {
public:
virtual ~IGunHal() {}
- virtual bool isTriggerDown() = 0;
- virtual bool isButtonDown() = 0;
- virtual void shortDelay() = 0;
- virtual void longDelay() = 0;
- virtual void ledOn() = 0;
- virtual void ledOff() = 0;
+
+ /*
+ * the 'loop' method shall be called each 10ms
+ */
+ virtual void setupHeartbeat() = 0;
+
virtual void laserOn() = 0;
virtual void laserOff() = 0;
virtual void vibrationOn() = 0;
virtual void vibrationOff() = 0;
- virtual void deepSleep() = 0;
+ virtual uint16_t getBatteryVoltageMv() = 0;
+ virtual bool isCharging() = 0;
+
};
\ No newline at end of file
diff --git a/lib/Domain/Gun/IGunUi.hpp b/lib/Domain/Gun/IGunUi.hpp
new file mode 100644
index 0000000..1933261
--- /dev/null
+++ b/lib/Domain/Gun/IGunUi.hpp
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright (c) 2023 Aurélien Labrosse
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+
+class IGunUi {
+
+public:
+ virtual void displaySplash(uint16_t timeoutMs) = 0;
+ virtual void displayBatteryStatus(uint16_t mv, uint8_t percent) = 0;
+ virtual void displayChargingStatus(bool isCharging) = 0;
+ virtual void displayShootCount(uint16_t shootCount) = 0;
+};
\ No newline at end of file
diff --git a/lib/Domain/Gun/SSD1306Ui.hpp b/lib/Domain/Gun/SSD1306Ui.hpp
new file mode 100644
index 0000000..abc6486
--- /dev/null
+++ b/lib/Domain/Gun/SSD1306Ui.hpp
@@ -0,0 +1,94 @@
+/*
+ *
+ * Copyright (c) 2023 Aurélien Labrosse
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+
+#include
+
+#include
+#include
+#include
+
+#define SCREEN_WIDTH 128 // OLED display width, in pixels
+#define SCREEN_HEIGHT 32 // OLED display height, in pixels
+#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
+// 0x3D for 128x64, 0x3C for 128x32
+#define SCREEN_ADDRESS 0x3C
+
+static Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
+
+/**
+ * @brief UI on a SSD1306 OLED display wired over I²C bus
+ *
+ */
+class SSD1306Ui : public IGunUi {
+public:
+ SSD1306Ui() {
+
+ if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
+ for (;;)
+ ; // Don't proceed, loop forever
+ }
+ }
+ void displaySplash(uint16_t timeoutMs) override {
+ display.clearDisplay();
+ display.setTextColor(WHITE);
+ display.setTextSize(1);
+ display.setFont(&FreeMonoOblique9pt7b);
+ display.setCursor(9, 11);
+ display.println("5 in a row");
+ display.setCursor(4, 27);
+ display.println(" # # # # #");
+ display.display();
+ delay(timeoutMs);
+ display.clearDisplay();
+ }
+
+ void displayBatteryStatus(uint16_t mv, uint8_t percent) override {
+ display.setFont(NULL);
+ display.fillRect(79, 0, 59, 24, 0); // clear
+ display.setCursor(100, 2);
+ display.print(percent);
+ display.println("%");
+
+ // prepare font for next shoot count display
+ display.setFont(&FreeMonoBold18pt7b);
+ }
+ void displayChargingStatus(bool isCharging) override {
+ display.setCursor(80, 15);
+ if (isCharging) {
+ display.println("charging");
+ } else {
+ display.println(" ");
+ }
+ }
+
+ void displayShootCount(uint16_t count) override {
+ display.fillRect(11, 0, 68, 30, 0); // clear
+
+ if (count > 999)
+ count = 0;
+
+ if (count < 10) {
+ display.setCursor(15, 25);
+ } else {
+ display.setCursor(10, 25);
+ }
+ display.setFont(&FreeMonoBold18pt7b);
+ display.print(count);
+ display.display();
+ }
+};
\ No newline at end of file
diff --git a/lib/Domain/Gun/Trigger.cpp b/lib/Domain/Gun/Trigger.cpp
new file mode 100644
index 0000000..b71006c
--- /dev/null
+++ b/lib/Domain/Gun/Trigger.cpp
@@ -0,0 +1,18 @@
+/*
+ *
+ * Copyright (c) 2023 Aurélien Labrosse
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
diff --git a/lib/Domain/Gun/Trigger.hpp b/lib/Domain/Gun/Trigger.hpp
new file mode 100644
index 0000000..d76a102
--- /dev/null
+++ b/lib/Domain/Gun/Trigger.hpp
@@ -0,0 +1,43 @@
+/*
+ *
+ * Copyright (c) 2023 Aurélien Labrosse
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+
+#include
+
+class Trigger : public Contactor {
+public:
+ Trigger() : Contactor(), shootCycleCountdown(0) {}
+
+ // counter for ticks while firing
+ uint8_t shootCycleCountdown;
+
+ void decreaseCycleCount() {
+ if (shootCycleCountdown > 0) {
+ shootCycleCountdown--;
+ }
+ }
+
+ void onDown(long now) override {
+ if (shootCycleCountdown == 0) {
+ shootCount += 1;
+ LASER_ON();
+ OUT2_ON();
+ shootCycleCountdown = SHOOT_DURATION_CYCLE;
+ }
+ Contactor::onDown(now);
+ }
+};
diff --git a/src/GunApp.cpp b/src/GunApp.cpp
index bbb263c..7e3d12c 100644
--- a/src/GunApp.cpp
+++ b/src/GunApp.cpp
@@ -16,282 +16,22 @@
*/
#include
-#include
#include
#include
#include
-#define TRIGGER_PIN 2
-#define BUTTON2_PIN 3
+#include
+#include
-#define LASER_PIN 10
-#define LASER_ON() PORTB |= (1 << PB2);
-#define LASER_OFF() PORTB &= ~(1 << PB2);
-
-#define OUT2_PIN 6
-#define OUT2_ON() PORTD |= (1 << PD6);
-#define OUT2_OFF() PORTD &= ~(1 << PD6);
-
-#define BATTERY_VOLTAGE_PIN A3
-#define CHARGING_STATE_PIN A2
-
-#define SHOOT_DURATION_CYCLE 5
-#define MIN_BAT_VOLTAGE 3000
-#define MAX_BAT_VOLTAGE 4120
-
-#define SCREEN_WIDTH 128 // OLED display width, in pixels
-#define SCREEN_HEIGHT 32 // OLED display height, in pixels
-#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
-// 0x3D for 128x64, 0x3C for 128x32
-#define SCREEN_ADDRESS 0x3C
-
-Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
-
-volatile uint16_t hitcount;
-uint16_t previoushitcout;
-volatile uint16_t shootCount;
-
-#define BSP_TICKS_PER_SEC 100
-#define F_CPU 16000000L
-#define CYCLES_BETWEEN_UI_UPDATE 10
-
-// display API
-static void displayBatteryStatus();
-static void displayShootCount();
-
-void setup();
-static void setupHeartbeat();
-
-// counter for ticks between battery display update
-uint16_t updateBatteryDisplayCycleCount;
-
-class Trigger : public Contactor {
-public:
- Trigger() : Contactor(), shootCycleCountdown(0) {}
-
- // counter for ticks while firing
- uint8_t shootCycleCountdown;
-
- void decreaseCycleCount() {
- if (shootCycleCountdown > 0) {
- shootCycleCountdown--;
- }
- }
-
- void onDown(long now) override {
- if (shootCycleCountdown == 0) {
- shootCount += 1;
- LASER_ON();
- OUT2_ON();
- shootCycleCountdown = SHOOT_DURATION_CYCLE;
- }
- Contactor::onDown(now);
- }
-};
-
-class Button : public Contactor {
-
-public:
- Button() : Contactor(), calibrationMode(false) {}
-
- bool calibrationMode;
- void onShortPress() override { shootCount = 0; }
- void onLongPress() override { calibrationMode = !calibrationMode; }
-};
-
-Trigger trigger;
-Button button;
-
-volatile Contactor::Event pendingTriggerEvent;
-void triggerInterruptHandler() {
- if (bit_is_set(PIND, PD2)) {
- pendingTriggerEvent = Contactor::Event::Released;
- } else {
- pendingTriggerEvent = Contactor::Event::Pressed;
- }
-}
-
-volatile Contactor::Event pendingButtonEvent;
-void buttonInterruptHandler() {
- if (bit_is_set(PIND, PD3)) {
- pendingButtonEvent = Contactor::Event::Released;
- } else {
- pendingButtonEvent = Contactor::Event::Pressed;
- }
-}
+Atmega328pHal hal;
+Gun gun(&hal);
ISR(TIMER2_COMPA_vect) {}
-long now = 0;
-
-void loop(void) {
-
- now += 10; // 10ms per loop thanks to timer2
-
- trigger.decreaseCycleCount();
-
- cli();
- trigger.pendingEvent = pendingTriggerEvent;
- button.pendingEvent = pendingButtonEvent;
- pendingTriggerEvent = Contactor::Event::NoEvent;
- pendingButtonEvent = Contactor::Event::NoEvent;
- sei();
-
- trigger.processPendingEvent(now);
- button.processPendingEvent(now);
-
- button.checkForLongPress(now);
-
- if (button.calibrationMode) {
- LASER_ON();
- OUT2_OFF();
-
- } else {
- if (trigger.shootCycleCountdown == 0) {
- LASER_OFF();
- OUT2_OFF();
- displayShootCount();
- }
-
- if (updateBatteryDisplayCycleCount > 0) {
- updateBatteryDisplayCycleCount--;
- }
- if (updateBatteryDisplayCycleCount == 0) {
- displayBatteryStatus();
- updateBatteryDisplayCycleCount = CYCLES_BETWEEN_UI_UPDATE;
- }
- }
-
- LowPower.idle(SLEEP_15MS, ADC_OFF, TIMER2_ON, TIMER1_ON, TIMER0_ON, SPI_OFF,
- USART0_OFF, TWI_OFF);
-}
-
-static void displayShootCount() {
- display.fillRect(11, 0, 68, 30, 0); // clear
-
- if (shootCount > 999)
- shootCount = 0;
-
- if (shootCount < 10) {
- display.setCursor(15, 25);
- } else {
- display.setCursor(10, 25);
- }
- display.print(shootCount);
- display.display();
-}
-
-static void displayBatteryStatus() {
-
- analogRead(BATTERY_VOLTAGE_PIN);
- float rawBatt = analogRead(BATTERY_VOLTAGE_PIN);
- rawBatt += analogRead(BATTERY_VOLTAGE_PIN);
- rawBatt += analogRead(BATTERY_VOLTAGE_PIN);
- rawBatt += analogRead(BATTERY_VOLTAGE_PIN);
- rawBatt /= 4;
-
- // https://rickkas7.github.io/DisplayGenerator/index.html
-
- float battMv = (5000 / 1023.f) * rawBatt;
- bool isCharging = (digitalRead(CHARGING_STATE_PIN) == HIGH);
-
-#if defined(SERIAL_OUTPUT)
- Serial.print(rawBatt);
- Serial.print(';');
- Serial.print(battMv);
- Serial.print(';');
- Serial.println(isCharging ? "c" : "n");
- delay(2);
-#endif
-
- uint8_t battPercent = map(battMv, MIN_BAT_VOLTAGE, MAX_BAT_VOLTAGE, 0, 100);
-
- display.setFont(NULL);
- display.fillRect(79, 0, 59, 24, 0); // clear
- display.setCursor(100, 2);
- display.print(battPercent);
- display.println("%");
- if (isCharging) {
- display.setCursor(80, 15);
- display.println("charging");
- }
-
- // prepare font for next shoot count display
- display.setFont(&FreeMonoBold18pt7b);
-}
-
-static void setupHeartbeat() {
-
- // set Timer2 in CTC mode, 1/1024 prescaler, start the timer ticking...
- TCCR2A = (1U << WGM21) | (0U << WGM20);
- TCCR2B = (1U << CS22) | (1U << CS21) | (1U << CS20); // 1/2^10
- ASSR &= ~(1U << AS2);
- TIMSK2 = (1U << OCIE2A); // enable TIMER2 compare Interrupt
- TCNT2 = 0U;
-
- // set the output-compare register based on the desired tick frequency
- OCR2A = (F_CPU / BSP_TICKS_PER_SEC / 1024U) - 1U;
-}
+void loop(void) { gun.loop(); }
void setup(void) {
-
-#if defined(SERIAL_OUTPUT)
- Serial.begin(115200);
- while (!Serial) {
- }
-#endif
- if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
-#if defined(SERIAL_OUTPUT)
- Serial.println(F("SSD1306 allocation failed"));
-#endif
- for (;;)
- ; // Don't proceed, loop forever
- }
-
- shootCount = 0;
- updateBatteryDisplayCycleCount = TICKS_BETWEEN_UI_UPDATE;
-
- pinMode(LASER_PIN, OUTPUT);
- digitalWrite(LASER_PIN, LOW);
-
- pinMode(OUT2_PIN, OUTPUT);
- digitalWrite(OUT2_PIN, LOW);
-
- pinMode(BATTERY_VOLTAGE_PIN, INPUT);
- pinMode(CHARGING_STATE_PIN, INPUT);
-
- pinMode(LED_BUILTIN, OUTPUT);
- digitalWrite(LED_BUILTIN, LOW);
-
-#if defined(SERIAL_OUTPUT)
- Serial.println("Ready!");
-#endif
-
- display.clearDisplay();
- display.setTextColor(WHITE);
- display.setTextSize(1);
- display.setFont(&FreeMonoOblique9pt7b);
- display.setCursor(9, 11);
- display.println("5 in a row");
- display.setCursor(4, 27);
- display.println(" # # # # #");
- display.display();
-
- delay(2000);
-
- display.clearDisplay();
- displayBatteryStatus();
- displayShootCount();
-
- pinMode(BUTTON2_PIN, INPUT_PULLUP);
- attachInterrupt(digitalPinToInterrupt(BUTTON2_PIN), buttonInterruptHandler,
- CHANGE);
-
- pinMode(TRIGGER_PIN, INPUT_PULLUP);
- attachInterrupt(digitalPinToInterrupt(TRIGGER_PIN), triggerInterruptHandler,
- CHANGE);
- cli();
- setupHeartbeat();
- sei();
+ hal.setupHeartbeat();
+ gun.setup();
}
\ No newline at end of file