148 lines
3.6 KiB
Arduino
148 lines
3.6 KiB
Arduino
// ===========================
|
|
// 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.
|
|
// ===========================
|
|
|
|
// ===========================
|
|
// ===== RECEIVER CODE ======
|
|
// ===========================
|
|
|
|
#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>
|
|
|
|
// Globals
|
|
volatile bool powerOnRx = false;
|
|
uint16_t lastSeq = 0;
|
|
unsigned long lastRecv = 0;
|
|
bool isArmed = false;
|
|
|
|
// OLED
|
|
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);
|
|
|
|
// CRC
|
|
uint16_t crc16_ccitt_rx(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
|
|
void IRAM_ATTR handlePowerBtnRx() {
|
|
powerOnRx = (digitalRead(RX_PIN_PWR_BTN) == LOW);
|
|
}
|
|
|
|
// Receive callback
|
|
void onDataRecvRx(const uint8_t *mac, const uint8_t *incomingData, int len) {
|
|
struct CommandMessage {
|
|
bool arm; bool launch; uint16_t seq; uint16_t crc;
|
|
} msg;
|
|
memcpy(&msg, incomingData, sizeof(msg));
|
|
uint16_t calc = crc16_ccitt_rx((uint8_t*)&msg, sizeof(msg) - sizeof(msg.crc));
|
|
if (calc != msg.crc || msg.seq <= lastSeq) return; // drop bad/replay
|
|
lastSeq = msg.seq;
|
|
lastRecv = millis();
|
|
isArmed = msg.arm;
|
|
if (isArmed && msg.launch) {
|
|
// fire mosfets
|
|
digitalWrite(RX_PIN_MOSFET1, HIGH);
|
|
digitalWrite(RX_PIN_MOSFET2, HIGH);
|
|
delay(1000);
|
|
digitalWrite(RX_PIN_MOSFET1, LOW);
|
|
digitalWrite(RX_PIN_MOSFET2, LOW);
|
|
isArmed = false;
|
|
}
|
|
}
|
|
|
|
void setupRx() {
|
|
Serial.begin(115200);
|
|
esp_task_wdt_init(5, true);
|
|
esp_task_wdt_add(NULL);
|
|
|
|
// Pins
|
|
pinMode(RX_PIN_MOSFET1, OUTPUT);
|
|
pinMode(RX_PIN_MOSFET2, OUTPUT);
|
|
pinMode(RX_PIN_CONTINUITY, INPUT);
|
|
pinMode(RX_PIN_PWR_BTN, INPUT_PULLUP);
|
|
pinMode(RX_LED_PWR, OUTPUT);
|
|
pinMode(RX_LED_CONN, OUTPUT);
|
|
attachInterrupt(RX_PIN_PWR_BTN, handlePowerBtnRx, CHANGE);
|
|
|
|
// OLED
|
|
Wire.begin(RX_OLED_SDA, RX_OLED_SCL);
|
|
if (oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
|
|
oled.clearDisplay(); oled.setTextSize(1);
|
|
oled.setTextColor(SSD1306_WHITE);
|
|
oled.setCursor(0,0);
|
|
oled.println("Receiver v2 ready"); oled.display();
|
|
}
|
|
|
|
// Wait for power
|
|
if (!powerOnRx) {
|
|
Serial.println("RX Power off. Halt.");
|
|
while (!powerOnRx) esp_task_wdt_reset();
|
|
}
|
|
digitalWrite(RX_LED_PWR, HIGH);
|
|
|
|
// Wi-Fi & ESP-NOW
|
|
WiFi.mode(WIFI_STA);
|
|
esp_now_init();
|
|
esp_now_register_recv_cb(onDataRecvRx);
|
|
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();
|
|
}
|
|
|
|
void loopRx() {
|
|
esp_task_wdt_reset();
|
|
ArduinoOTA.handle();
|
|
|
|
// Connection LED
|
|
digitalWrite(RX_LED_CONN, millis() - lastRecv < 1000);
|
|
|
|
// Continuity analog
|
|
int cont = analogRead(RX_PIN_CONTINUITY);
|
|
if (!cont || !isArmed) {
|
|
isArmed = false;
|
|
}
|
|
|
|
// Timeout
|
|
static unsigned long armTime = 0;
|
|
if (isArmed && armTime == 0) armTime = lastRecv;
|
|
if (isArmed && millis() - armTime > 30000) isArmed = false;
|
|
if (!isArmed) armTime = 0;
|
|
|
|
// OLED status
|
|
oled.clearDisplay();
|
|
oled.setCursor(0,0);
|
|
oled.printf("Conn:%d Armed:%d", digitalRead(RX_LED_CONN), isArmed);
|
|
oled.setCursor(0,10);
|
|
oled.printf("Cont:%d", cont);
|
|
oled.display();
|
|
|
|
delay(50);
|
|
}
|
|
|
|
void main() {
|
|
setupRx();
|
|
while (true) loopRx();
|
|
}
|