Files
Wireless-Launch-System/transmitter_v2.ino
T

195 lines
5.1 KiB
Arduino
Raw Normal View History

2026-10-02 09:50:56 -04:00
// ===========================
// Wireless Rocket Launch System v2
// Implements: MOSFET output, interrupts, state machine, non-blocking scheduler,
// ESP-NOW encryption, CRC16, sequence numbering, OTA updates, watchdog, heartbeat,
// RSSI monitoring, Adafruit SSD1306 OLED UI.
// ===========================
#include <Arduino.h>
#include <WiFi.h>
#include <esp_now.h>
#include <esp_task_wdt.h>
#include <ArduinoOTA.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>
// ======= Pin Definitions =======
// Transmitter pins
#define TX_PIN_ARM_SWITCH 14 // toggle, interrupt
#define TX_PIN_LAUNCH_BTN 15 // push, interrupt
#define TX_PIN_POWER_BTN 16 // push, interrupt
#define TX_LED_PWR 26
#define TX_LED_ARMED 12
#define TX_LED_CONN 27
#define TX_LED_PULSE 13
#define TX_OLED_SDA SDA
#define TX_OLED_SCL SCL
// Receiver pins
#define RX_PIN_MOSFET1 14 // MOSFET channel 1
#define RX_PIN_MOSFET2 25 // MOSFET channel 2 (redundant)
#define RX_PIN_CONTINUITY 15 // analog sense via ADC
#define RX_PIN_PWR_BTN 17 // momentary, interrupt
#define RX_LED_PWR 26
#define RX_LED_CONN 27
#define RX_OLED_SDA SDA
#define RX_OLED_SCL SCL
// OLED settings
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);
// ======= ESP-NOW Encryption Key =======
uint8_t espnow_key[16] = {
0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10
};
// ======= Packet Structure =======
struct __attribute__((packed)) CommandMessage {
bool arm;
bool launch;
uint16_t seq;
uint16_t crc;
};
// ======= Globals =======
volatile bool armRequested = false;
volatile bool launchRequested = false;
volatile bool powerOn = false;
uint16_t txSeq = 0;
unsigned long lastSend = 0;
// Transmitter MAC (to be filled with receiver MAC)
uint8_t peerMac[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
// ======= CRC16-CCITT =======
uint16_t crc16_ccitt(const uint8_t *data, size_t len) {
uint16_t crc = 0xFFFF;
while (len--) {
crc ^= (uint16_t)*data++ << 8;
for (uint8_t i = 0; i < 8; i++) {
if (crc & 0x8000) crc = (crc << 1) ^ 0x1021;
else crc <<= 1;
}
}
return crc;
}
// ======= Interrupt Handlers =======
void IRAM_ATTR handleArmSwitch() {
armRequested = (digitalRead(TX_PIN_ARM_SWITCH) == LOW);
}
void IRAM_ATTR handleLaunchBtn() {
if (armRequested && digitalRead(TX_PIN_LAUNCH_BTN) == LOW) {
launchRequested = true;
}
}
void IRAM_ATTR handlePowerBtn() {
powerOn = (digitalRead(TX_PIN_POWER_BTN) == LOW);
}
// ======= ESP-NOW Send Callback =======
void onDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
digitalWrite(TX_LED_CONN, status == ESP_NOW_SEND_SUCCESS);
}
// ======= Setup =======
void setup() {
// Serial & Watchdog
Serial.begin(115200);
esp_task_wdt_init(5, true);
esp_task_wdt_add(NULL);
// Pins
pinMode(TX_PIN_ARM_SWITCH, INPUT_PULLUP);
pinMode(TX_PIN_LAUNCH_BTN, INPUT_PULLUP);
pinMode(TX_PIN_POWER_BTN, INPUT_PULLUP);
pinMode(TX_LED_PWR, OUTPUT);
pinMode(TX_LED_ARMED, OUTPUT);
pinMode(TX_LED_CONN, OUTPUT);
pinMode(TX_LED_PULSE, OUTPUT);
// Attach interrupts
attachInterrupt(TX_PIN_ARM_SWITCH, handleArmSwitch, CHANGE);
attachInterrupt(TX_PIN_LAUNCH_BTN, handleLaunchBtn, FALLING);
attachInterrupt(TX_PIN_POWER_BTN, handlePowerBtn, CHANGE);
// OLED
Wire.begin(TX_OLED_SDA, TX_OLED_SCL);
if (display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0,0);
display.println("Transmitter v2 ready");
display.display();
}
// Power LED
if (!powerOn) {
Serial.println("Power button not pressed. Halting.");
while (!powerOn) esp_task_wdt_reset();
}
digitalWrite(TX_LED_PWR, HIGH);
// Wi‑Fi & ESP-NOW
WiFi.mode(WIFI_STA);
esp_now_init();
esp_now_register_send_cb(onDataSent);
esp_now_peer_info_t peer = {};
memcpy(peer.peer_addr, peerMac, 6);
peer.channel = 0;
peer.encrypt = true;
memcpy(peer.lmk, espnow_key, 16);
esp_now_add_peer(&peer);
// OTA
ArduinoOTA.begin();
}
// ======= Main Loop =======
void loop() {
esp_task_wdt_reset();
// OTA handling
ArduinoOTA.handle();
// State updates
digitalWrite(TX_LED_ARMED, armRequested);
// Send heartbeat/command every 100ms
if (millis() - lastSend >= 100) {
lastSend = millis();
CommandMessage msg;
msg.arm = armRequested;
msg.launch = launchRequested;
msg.seq = txSeq++;
msg.crc = crc16_ccitt((uint8_t*)&msg, sizeof(msg) - sizeof(msg.crc));
esp_now_send(peerMac, (uint8_t*)&msg, sizeof(msg));
launchRequested = false;
// Pulse LED on launch request
if (msg.launch) {
analogWrite(TX_LED_PULSE, 255);
} else {
analogWrite(TX_LED_PULSE, 0);
}
// Optionally read and display RSSI
int8_t rssi;
esp_now_fetch_peer(peerMac, nullptr);
esp_now_get_peer_rssi(peerMac, &rssi);
display.clearDisplay();
display.setCursor(0,0);
display.printf("ARM:%d LAUNCH:%d", msg.arm, msg.launch);
display.setCursor(0,10);
display.printf("RSSI:%ddBm", rssi);
display.display();
}
}