454 lines
11 KiB
C++
454 lines
11 KiB
C++
/*
|
|
DigitalReadSerial
|
|
|
|
Reads a digital input on pin 2, prints the result to the Serial Monitor
|
|
|
|
This example code is in the public domain.
|
|
|
|
https://www.arduino.cc/en/Tutorial/BuiltInExamples/DigitalReadSerial
|
|
*/
|
|
#include <SPI.h>
|
|
#include <Wire.h>
|
|
|
|
#define _ASYNC_MQTT_LOGLEVEL_ 1
|
|
|
|
#define WIFI_SSID {WIFI_SSID}
|
|
#define WIFI_PASSWORD {WIFI_PKEY}
|
|
#define MQTT_HOST {MQTT_HOST}
|
|
#define MQTT_PORT {MQTT_PORT}
|
|
const char *SubTopic = "arena/1";
|
|
|
|
#include <WiFi.h>
|
|
|
|
extern "C"
|
|
{
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/timers.h"
|
|
}
|
|
#include <AsyncMQTT_ESP32.h>
|
|
|
|
#include "CRC16.h"
|
|
|
|
int read_line = 22;
|
|
|
|
int rx2 = 16;
|
|
int tx2 = 17;
|
|
|
|
typedef struct {
|
|
uint8_t type;
|
|
uint8_t controller_version;
|
|
uint8_t unknown[12];
|
|
uint16_t crc;
|
|
} pkt_init_t;
|
|
|
|
typedef struct {
|
|
uint8_t type;
|
|
uint8_t unknown[13];
|
|
uint16_t crc;
|
|
} pkt_init_resp_t;
|
|
|
|
typedef struct {
|
|
uint8_t type;
|
|
uint8_t unknown[13];
|
|
uint16_t crc;
|
|
} pkt_init_2_t;
|
|
|
|
typedef struct {
|
|
uint8_t type;
|
|
uint8_t ack_type;
|
|
uint8_t unknown_1;
|
|
uint8_t unknown_2[7];
|
|
uint8_t unknown_3;
|
|
uint8_t unknown_4[3];
|
|
uint16_t crc;
|
|
} pkt_ack_t;
|
|
|
|
typedef struct {
|
|
uint8_t type;
|
|
uint8_t unknown[29];
|
|
uint16_t crc;
|
|
} pkt_init_3_resp_t;
|
|
|
|
CRC16 crc = CRC16(0x1021, 0xFFFF, 0x0000, false, false);
|
|
|
|
void parse_init(uint8_t* pkt, size_t len) {
|
|
pkt_init_t parsed;
|
|
parsed.controller_version = pkt[1];
|
|
memcpy(parsed.unknown, &pkt[2], 12);
|
|
parsed.crc = (pkt[14] << 8) + (pkt[15] << 0);
|
|
crc.restart();
|
|
crc.add(pkt, 14);
|
|
uint16_t calc_crc = crc.calc();
|
|
if (calc_crc != parsed.crc) {
|
|
Serial.printf("Got init packet with bad crc(%04x != %04x)\n", calc_crc, parsed.crc);
|
|
} else {
|
|
Serial.printf("Got init packet with matching crc(%04x)\n", calc_crc);
|
|
}
|
|
|
|
pkt_init_resp_t response;
|
|
response.type = 0x03;
|
|
response.unknown[0] = 0x01;
|
|
response.unknown[1] = 0x64;
|
|
response.unknown[2] = 0x00;
|
|
response.unknown[3] = 0x00;
|
|
response.unknown[4] = 0x00;
|
|
response.unknown[5] = 0x00;
|
|
response.unknown[6] = 0xBA;
|
|
response.unknown[7] = 0x00;
|
|
response.unknown[8] = 0x7C;
|
|
response.unknown[9] = 0x1C;
|
|
response.unknown[10] = 0x88;
|
|
response.unknown[11] = 0x00;
|
|
response.unknown[12] = 0x00;
|
|
|
|
crc.restart();
|
|
crc.add((uint8_t*)&response, 14);
|
|
response.crc = htons(crc.calc());
|
|
|
|
digitalWrite(read_line, HIGH);
|
|
Serial2.write((uint8_t*)&response, 16);
|
|
delayMicroseconds(100);
|
|
digitalWrite(read_line, LOW);
|
|
}
|
|
|
|
void parse_init_2(uint8_t* pkt, size_t len) {
|
|
pkt_init_2_t parsed;
|
|
memcpy(parsed.unknown, &pkt[1], 13);
|
|
parsed.crc = (pkt[14] << 8) + (pkt[15] << 0);
|
|
crc.restart();
|
|
crc.add(pkt, 14);
|
|
uint16_t calc_crc = crc.calc();
|
|
if (calc_crc != parsed.crc) {
|
|
Serial.printf("Got init_2 packet with bad crc(%04x != %04x)\n", calc_crc, parsed.crc);
|
|
} else {
|
|
Serial.printf("Got init_2 packet with matching crc(%04x)\n", calc_crc);
|
|
}
|
|
|
|
pkt_ack_t response;
|
|
response.type = 0x02;
|
|
response.ack_type = pkt[0];
|
|
response.unknown_1 = 0x01;
|
|
memset(response.unknown_2, 0, 7);
|
|
response.unknown_3 = 0x01;
|
|
memset(response.unknown_4, 0, 3);
|
|
|
|
crc.restart();
|
|
crc.add((uint8_t*)&response, 14);
|
|
response.crc = htons(crc.calc());
|
|
|
|
digitalWrite(read_line, HIGH);
|
|
Serial2.write((uint8_t*)&response, 16);
|
|
delayMicroseconds(100);
|
|
digitalWrite(read_line, LOW);
|
|
}
|
|
|
|
void parse_init_3(uint8_t* pkt, size_t len) {
|
|
crc.restart();
|
|
crc.add(pkt, 30);
|
|
uint16_t parsed_crc = (pkt[30] << 8) + (pkt[31] << 0);
|
|
uint16_t calc_crc = crc.calc();
|
|
|
|
if (parsed_crc != calc_crc) {
|
|
Serial.printf("Got init_3 packet with bad crc(%04x != %04x\n", calc_crc, parsed_crc);
|
|
} else {
|
|
Serial.printf("Got init_3 packet with matching crc(%04x)\n", calc_crc);
|
|
}
|
|
|
|
pkt_init_3_resp_t response;
|
|
response.type = 0x04;
|
|
memset(response.unknown, 0, 29);
|
|
response.unknown[1] = 0x14;
|
|
|
|
crc.restart();
|
|
crc.add((uint8_t*)&response, 30);
|
|
response.crc = htons(crc.calc());
|
|
|
|
digitalWrite(read_line, HIGH);
|
|
Serial2.write((uint8_t*)&response, 32);
|
|
delayMicroseconds(200);
|
|
digitalWrite(read_line, LOW);
|
|
}
|
|
|
|
void send_ack(uint8_t ack_type, uint8_t unknown) {
|
|
uint8_t response[32];
|
|
memset(response, 0, 32);
|
|
response[0] = 0x02;
|
|
response[1] = ack_type;
|
|
response[2] = unknown;
|
|
response[10] = unknown;
|
|
|
|
crc.restart();
|
|
crc.add(response, 30);
|
|
uint16_t ack_crc = crc.calc();
|
|
|
|
response[30] = (ack_crc >> 8 & 0xFF);
|
|
response[31] = (ack_crc >> 0 & 0xFF);
|
|
|
|
digitalWrite(read_line, HIGH);
|
|
Serial2.write((uint8_t*)&response, 32);
|
|
delayMicroseconds(200);
|
|
digitalWrite(read_line, LOW);
|
|
}
|
|
|
|
void send_state(uint8_t control_flags, uint32_t timer) {
|
|
uint8_t response[32];
|
|
memset(response, 0, 32);
|
|
response[0] = 0x53;
|
|
response[1] = control_flags;
|
|
response[2] = 0x00;
|
|
response[3] = 0x00;
|
|
response[4] = (timer >> 24 & 0xFF);
|
|
response[5] = (timer >> 16 & 0xFF);
|
|
response[6] = (timer >> 8 & 0xFF);
|
|
response[7] = (timer >> 0 & 0xFF);
|
|
response[8] = 0x03;
|
|
|
|
crc.restart();
|
|
crc.add(response, 30);
|
|
uint16_t ack_crc = crc.calc();
|
|
|
|
response[30] = (ack_crc >> 8 & 0xFF);
|
|
response[31] = (ack_crc >> 0 & 0xFF);
|
|
|
|
digitalWrite(read_line, HIGH);
|
|
Serial2.write((uint8_t*)&response, 32);
|
|
delayMicroseconds(200);
|
|
digitalWrite(read_line, LOW);
|
|
}
|
|
|
|
typedef struct __attribute__((packed)) {
|
|
uint8_t type;
|
|
uint8_t unknown_1;
|
|
uint16_t system_status;
|
|
uint8_t unknown_2[2];
|
|
uint8_t field_status;
|
|
uint8_t unknown_3[11];
|
|
uint8_t name[10];
|
|
uint8_t version[2];
|
|
uint16_t crc;
|
|
} pkt_poll_t;
|
|
|
|
volatile uint8_t control_flags = 0xC9;
|
|
volatile uint32_t timer = 0x00002000;
|
|
#define POLL_COUNT_MAX 5
|
|
|
|
void parse_poll(uint8_t* pkt, size_t len) {
|
|
pkt_poll_t* parsed = (pkt_poll_t*)pkt;
|
|
crc.restart();
|
|
crc.add(pkt, 30);
|
|
uint16_t crc_calc = crc.calc();
|
|
|
|
if (htons(parsed->crc) != crc_calc) {
|
|
Serial.printf("parsed poll with invalid CRC 0x%04x, calculated to be 0x%04x\n", htons(parsed->crc), crc_calc);
|
|
} else {
|
|
//Serial.printf("parsed poll with valid CRC 0x%04x, system status 0x%04x, field status 0x%02x, team name %s\n", htons(parsed->crc), htons(parsed->system_status), parsed->field_status, parsed->name);
|
|
}
|
|
|
|
if (parsed->field_status == control_flags) {
|
|
send_ack(0xA7, 0x01);
|
|
} {
|
|
send_state(control_flags, timer);
|
|
}
|
|
}
|
|
|
|
void on_rx(void) {
|
|
uint8_t pkt[4];
|
|
int n = Serial.available();
|
|
if (n < 4) {
|
|
return;
|
|
}
|
|
int read = Serial.readBytes(pkt, n);
|
|
sscanf((const char*)pkt, "0x%2hhx", &control_flags);
|
|
Serial.printf("Parsed new control flags: 0x%02x\n", control_flags);
|
|
}
|
|
|
|
void on_rx_2(void) {
|
|
uint8_t pkt[32];
|
|
int n = Serial2.available();
|
|
if (n < 16) {
|
|
return;
|
|
}
|
|
int read = Serial2.readBytes(pkt, n);
|
|
switch (pkt[0]) {
|
|
case 0xA7:
|
|
parse_poll(pkt, read);
|
|
break;
|
|
case 0x0A:
|
|
parse_init(pkt, read);
|
|
break;
|
|
case 0x13:
|
|
parse_init_2(pkt, read);
|
|
break;
|
|
case 0x11:
|
|
parse_init_3(pkt, read);
|
|
break;
|
|
case 0x02:
|
|
switch (pkt[1]){
|
|
case 0x04:
|
|
send_ack(0x02, 0x00);
|
|
break;
|
|
default:
|
|
Serial.printf("Unknown ack 0x%x\n", pkt[1]);
|
|
}
|
|
break;
|
|
default:
|
|
Serial.printf("Unknown command 0x%x\n", pkt[0]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
AsyncMqttClient mqttClient;
|
|
TimerHandle_t mqttReconnectTimer;
|
|
TimerHandle_t wifiReconnectTimer;
|
|
|
|
void connectToWifi()
|
|
{
|
|
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
|
}
|
|
|
|
void connectToMqtt()
|
|
{
|
|
mqttClient.connect();
|
|
}
|
|
|
|
void WiFiEvent(WiFiEvent_t event)
|
|
{
|
|
switch (event)
|
|
{
|
|
#if USING_CORE_ESP32_CORE_V200_PLUS
|
|
|
|
case ARDUINO_EVENT_WIFI_READY:
|
|
Serial.println("WiFi ready");
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_STA_START:
|
|
Serial.println("WiFi STA starting");
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
|
|
Serial.println("WiFi STA connected");
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
|
|
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
|
Serial.println("WiFi connected");
|
|
Serial.print("IP address: ");
|
|
Serial.println(WiFi.localIP());
|
|
connectToMqtt();
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_STA_LOST_IP:
|
|
Serial.println("WiFi lost IP");
|
|
break;
|
|
|
|
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
|
Serial.println("WiFi lost connection");
|
|
xTimerStop(mqttReconnectTimer, 0); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
|
|
xTimerStart(wifiReconnectTimer, 0);
|
|
break;
|
|
#else
|
|
|
|
case SYSTEM_EVENT_STA_GOT_IP:
|
|
Serial.println("WiFi connected");
|
|
Serial.println("IP address: ");
|
|
Serial.println(WiFi.localIP());
|
|
connectToMqtt();
|
|
break;
|
|
|
|
case SYSTEM_EVENT_STA_DISCONNECTED:
|
|
Serial.println("WiFi lost connection");
|
|
xTimerStop(mqttReconnectTimer, 0); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
|
|
xTimerStart(wifiReconnectTimer, 0);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void printSeparationLine()
|
|
{
|
|
Serial.println("************************************************");
|
|
}
|
|
|
|
void onMqttConnect(bool sessionPresent)
|
|
{
|
|
Serial.println("mqtt connected");
|
|
mqttClient.subscribe(SubTopic, 2);
|
|
}
|
|
|
|
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason)
|
|
{
|
|
(void) reason;
|
|
|
|
if (WiFi.isConnected())
|
|
{
|
|
xTimerStart(mqttReconnectTimer, 0);
|
|
}
|
|
}
|
|
|
|
void onMqttSubscribe(const uint16_t& packetId, const uint8_t& qos)
|
|
{
|
|
Serial.println("subscribed to topic");
|
|
}
|
|
|
|
void onMqttUnsubscribe(const uint16_t& packetId)
|
|
{
|
|
}
|
|
|
|
void onMqttMessage(char* topic, char* payload, const AsyncMqttClientMessageProperties& properties,
|
|
const size_t& len, const size_t& index, const size_t& total)
|
|
{
|
|
if(len < 4) {
|
|
Serial.printf("not enough data: %d", len);
|
|
}
|
|
sscanf((const char*)payload, "0x%2hhx", &control_flags);
|
|
Serial.printf("Parsed new control flags: 0x%02x\n", control_flags);
|
|
}
|
|
|
|
void onMqttPublish(const uint16_t& packetId)
|
|
{
|
|
Serial.println("Publish acknowledged.");
|
|
Serial.print(" packetId: ");
|
|
Serial.println(packetId);
|
|
}
|
|
|
|
void setup()
|
|
{
|
|
Serial.begin(115200);
|
|
Serial2.begin(1562500, SERIAL_8N1, rx2, tx2);
|
|
|
|
pinMode(read_line, OUTPUT);
|
|
digitalWrite(read_line, LOW);
|
|
Serial2.onReceive(on_rx_2);
|
|
Serial.onReceive(on_rx);
|
|
Serial.println("Started");
|
|
|
|
while (!Serial && millis() < 5000);
|
|
|
|
delay(500);
|
|
|
|
mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0,
|
|
reinterpret_cast<TimerCallbackFunction_t>(connectToMqtt));
|
|
wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0,
|
|
reinterpret_cast<TimerCallbackFunction_t>(connectToWifi));
|
|
|
|
WiFi.onEvent(WiFiEvent);
|
|
|
|
mqttClient.onConnect(onMqttConnect);
|
|
mqttClient.onDisconnect(onMqttDisconnect);
|
|
mqttClient.onSubscribe(onMqttSubscribe);
|
|
mqttClient.onUnsubscribe(onMqttUnsubscribe);
|
|
mqttClient.onMessage(onMqttMessage);
|
|
mqttClient.onPublish(onMqttPublish);
|
|
|
|
mqttClient.setServer(MQTT_HOST, MQTT_PORT);
|
|
|
|
connectToWifi();
|
|
}
|
|
|
|
void loop()
|
|
{
|
|
}
|