diff --git a/lib/comms/bus.cpp b/lib/comms/bus.cpp new file mode 100644 index 0000000..d9af703 --- /dev/null +++ b/lib/comms/bus.cpp @@ -0,0 +1,92 @@ +#include "bus.h" +#include + +bool initUARTs(Config &config) +{ + if (config.uart0.enabled) + { + Uart0.end(); + Uart0.begin(config.uart0.clock_freq, SERIAL_8N1, config.uart0.rx, + config.uart0.tx); + } + + if (config.uart1.enabled) + { + Uart1.end(); + Uart1.begin(config.uart1.clock_freq, SERIAL_8N1, config.uart1.rx, + config.uart1.tx); + } + + return true; +} + +SPIClass &hspi = *(new SPIClass(HSPI)); // not usable until initSPIs + +bool initSPIs(Config &config) +{ + if (config.spi1.enabled) + { + delete (&hspi); + hspi = *(new SPIClass(config.spi1.bus_num)); + if (config.spi1.clock_freq > 0) + { + hspi.setFrequency(config.spi1.clock_freq); + } + + // if all the pins are -1, then will use the default for SPI bus_num + hspi.begin(config.spi1.clk, config.spi1.miso, config.spi1.mosi); + Serial.printf("Initialized SPI%d: SC:%d MISO:%d MOSI:%d clock:%d\n", + (int)config.spi1.bus_num, (int)config.spi1.clk, + (int)config.spi1.miso, (int)config.spi1.mosi, + (int)config.spi1.clock_freq); + } + + return true; +} + +uint8_t _scanSupportedDevicesOnWire(TwoWire &w, int bus_num); + +uint8_t wireDevices; +uint8_t wire1Devices; + +bool initWires(Config &config) +{ + wireDevices = _scanSupportedDevicesOnWire(Wire, 0); + + if (config.wire1.enabled) + { +#if SOC_I2C_NUM > 1 + // if you want to use default pins, configure -1 + // if you want to use default clock speed, configure 0 + if (!Wire1.begin(config.wire1.sda, config.wire1.scl, config.wire1.clock_freq)) + { + Serial.println("Failed to initialize Wire1"); + return false; + } + + wire1Devices = _scanSupportedDevicesOnWire(Wire1, 1); +#endif + } + + return true; +} + +uint8_t _scanSupportedDevicesOnWire(TwoWire &w, int bus_num) +{ + uint8_t res = 0; + + for (int i = 0; known_i2c_devices[i].address > 0; i++) + { + w.beginTransmission(known_i2c_devices[i].address); + delay(2); + if (w.endTransmission() == 0) + { + Serial.printf("Found supported device on Wire%d: %s(%X)\n", bus_num, + known_i2c_devices[i].name.c_str(), + (int)known_i2c_devices[i].address); + res |= 1 << i; + } + } + + return res; +} diff --git a/lib/comms/bus.h b/lib/comms/bus.h new file mode 100644 index 0000000..b94599e --- /dev/null +++ b/lib/comms/bus.h @@ -0,0 +1,39 @@ +#pragma once +#include + +struct +{ + String name; + uint8_t address; +} known_i2c_devices[] = {{"HMC5883L", 0x1e}, {"QMC5883L", 0x0d}, {" last record ", 0}}; + +enum I2CDevices +{ + // powers of 2 + HMC5883L = 1, + QMC5883L = 2 +}; + +extern uint8_t wireDevices; +extern uint8_t wire1Devices; + +extern SPIClass &hspi; + +// abstract away a reference to Serial vs Serial0 vs Serial1, so it compiles +#ifndef ARDUINO_USB_CDC_ON_BOOT +#define Uart0 Serial +#else +#define Uart0 Serial0 +#endif + +#if SOC_UART_NUM > 1 +#define Uart1 Serial1 +#else +#define Uart1 Uart0 +#endif + +bool initSPIs(Config &config); + +bool initUARTs(Config &config); + +bool initWires(Config &config); diff --git a/lib/comms/comms.cpp b/lib/comms/comms.cpp index b31fe13..0814047 100644 --- a/lib/comms/comms.cpp +++ b/lib/comms/comms.cpp @@ -86,9 +86,8 @@ bool Comms::initComms(Config &c) if (c.listen_on_serial0.equalsIgnoreCase("readline")) { // comms using readline plaintext protocol - Comms0 = new ReadlineComms("UART0", SERIAL0); - SERIAL0.onReceive(_onReceive0, false); - SERIAL0.begin(115200); + Comms0 = new ReadlineComms("UART0", Uart0); + Uart0.onReceive(_onReceive0, false); Serial.println("Initialized communications on Serial0 using readline protocol"); } @@ -102,9 +101,8 @@ bool Comms::initComms(Config &c) if (c.listen_on_serial1.equalsIgnoreCase("readline")) { // comms using readline plaintext protocol - Comms1 = new ReadlineComms("UART1", Serial1); - Serial1.onReceive(_onReceive1, false); - Serial1.begin(115200); + Comms1 = new ReadlineComms("UART1", Uart1); + Uart1.onReceive(_onReceive1, false); Serial.println("Initialized communications on Serial1 using readline protocol"); } diff --git a/lib/comms/comms.h b/lib/comms/comms.h index 4154e36..b8d2905 100644 --- a/lib/comms/comms.h +++ b/lib/comms/comms.h @@ -5,14 +5,9 @@ #include #include +#include #include -#ifndef ARDUINO_USB_CDC_ON_BOOT -#define SERIAL0 Serial -#else -#define SERIAL0 Serial0 -#endif - #ifndef SCAN_MAX_RESULT_KHZ_SCALE // kHz scale: round frequency, so it fits into 2 bytes // 2500000 / 40 = 62500, scale 40 fits 2.5GHz into two bytes @@ -135,7 +130,7 @@ struct Comms struct NoopComms : Comms { - NoopComms() : Comms("no-op", SERIAL0) {}; + NoopComms() : Comms("no-op", Uart0) {}; virtual bool send(Message &) { return true; }; virtual void _onReceive() {}; diff --git a/lib/config/config.cpp b/lib/config/config.cpp index 28c8779..faadd48 100644 --- a/lib/config/config.cpp +++ b/lib/config/config.cpp @@ -170,6 +170,30 @@ bool Config::updateConfig(String key, String value) return true; } + if (key.equalsIgnoreCase("uart0")) + { + uart0 = BusConfig::configure(value); + return true; + } + + if (key.equalsIgnoreCase("uart1")) + { + uart1 = BusConfig::configure(value); + return true; + } + + if (key.equalsIgnoreCase("spi1")) + { + spi1 = BusConfig::configure(value); + return true; + } + + if (key.equalsIgnoreCase("wire1")) + { + wire1 = BusConfig::configure(value); + return true; + } + UPDATE_BOOL(is_host, key, value); UPDATE_BOOL(lora_enabled, key, value); @@ -521,6 +545,11 @@ bool Config::write_config(const char *path) f.println("lora_enabled = " + getConfig("lora_enabled")); + f.println("uart0 = " + getConfig("uart0")); + f.println("uart1 = " + getConfig("uart1")); + f.println("spi1 = " + getConfig("spi1")); + f.println("wire1 = " + getConfig("wire1")); + f.close(); return true; } @@ -572,6 +601,26 @@ String Config::getConfig(String key) return String(is_host ? "true" : "false"); } + if (key.equalsIgnoreCase("uart0")) + { + return uart0.toStr(); + } + + if (key.equalsIgnoreCase("uart1")) + { + return uart1.toStr(); + } + + if (key.equalsIgnoreCase("spi1")) + { + return spi1.toStr(); + } + + if (key.equalsIgnoreCase("wire1")) + { + return wire1.toStr(); + } + return ""; } @@ -642,3 +691,127 @@ ParseResult parse_config_line(String ln) return ParseResult(k, v); } + +BusConfig BusConfig::configure(String cfg) +{ + BusConfig c; + + if (cfg.equalsIgnoreCase("none")) + { + return c; + } + + int begin = 0; + int end, i; + + while ((i = findSepa(cfg, ",", begin, end)) >= 0) + { + String param = cfg.substring(begin, end); + begin = i; + int j = param.indexOf(":"); + if (j < 0) + { + Serial.printf("Expected ':' to be present in '%s' - ignoring config\n", + param); + continue; + } + + String k = param.substring(0, j); + param = param.substring(j + 1); + + k.toLowerCase(); + if (k.equals("enabled")) + { + c.enabled = !param.equalsIgnoreCase("false"); + continue; + } + + if (c.bus_type == SPI && k.equals("clk") || c.bus_type == WIRE && k.equals("scl")) + { + c.clk = param.toInt(); + continue; + } + + if (c.bus_type == SERIAL && k.equals("tx") || + c.bus_type == SPI && k.equals("mosi") || + c.bus_type == WIRE && k.equals("sda")) + { + c.tx = param.toInt(); + continue; + } + + if (c.bus_type == SERIAL && k.equals("rx") || + c.bus_type == SPI && k.equals("miso")) + { + c.rx = param.toInt(); + continue; + } + + if (c.bus_type != NONE) + { + Serial.printf("Unknown key: '%s'; ignoring config\n", k.c_str()); + continue; + } + + if (k.equals("none") || k.length() == 0) + { + continue; + } + + c.enabled = true; + + char bus_type = k.charAt(0); + switch (bus_type) + { + case 'u': + c.bus_type = UART; + break; + case 's': + c.bus_type = SPI; + break; + case 'w': + c.bus_type = WIRE; + break; + default: + c.bus_type = NONE; + c.enabled = false; + } + + c.bus_num = k.substring(1).toInt(); + c.clock_freq = param.toInt(); + } + + return c; +} + +String BusConfig::toStr() +{ + if (bus_type == NONE) + { + return "none"; + } + + String ret = String(bus_type == UART ? "u" + : bus_type == SPI ? "s" + : bus_type == WIRE ? "w" + : "none") + + String(bus_num); + + ret += ":" + String(clock_freq) + (enabled ? "" : ",enabled:false"); + switch (bus_type) + { + case UART: + ret += ",rx:" + String(rx) + ",tx:" + String(tx); + break; + case SPI: + ret += ",clk:" + String(clk) + ",mosi:" + String(mosi) + ",miso:" + String(miso); + break; + case WIRE: + ret += ",scl:" + String(scl) + ",sda:" + String(sda); + break; + default: + ret = "none"; + } + + return ret; +} diff --git a/lib/config/config.h b/lib/config/config.h index b52e740..7ef380e 100644 --- a/lib/config/config.h +++ b/lib/config/config.h @@ -53,6 +53,49 @@ struct LoRaConfig LoRaConfig *configureLora(String cfg); +struct BusConfig +{ + enum + { + NONE, // No bus + UART, // Serial + SPI, + WIRE // I2C + } bus_type; + + int8_t bus_num; + + bool enabled; + + uint32_t clock_freq; + + union + { + int8_t scl; + int8_t clk; + }; + + union + { + int8_t sda; + int8_t mosi; + int8_t tx; + }; + + union + { + int8_t miso; + int8_t rx; + }; + + BusConfig() + : bus_type(NONE), bus_num(-1), enabled(false), clock_freq(115200), clk(-1), + rx(-1), tx(-1) {}; + + static BusConfig configure(String cfg); + String toStr(); +}; + #ifndef FREQ_RX #define FREQ_RX 866 #endif @@ -84,6 +127,22 @@ LoRaConfig *configureLora(String cfg); #define DEFAULT_LORA_SF 7 #endif +#ifndef DEFAULT_UART0 +#define DEFAULT_UART0 "none" +#endif + +#ifndef DEFAULT_UART1 +#define DEFAULT_UART1 "u1:115200" +#endif + +#ifndef DEFAULT_SPI1 +#define DEFAULT_SPI1 "none" +#endif + +#ifndef DEFAULT_WIRE1 +#define DEFAULT_WIRE1 "none" +#endif + #define CREATE_MISSING_CONFIG true struct Config { @@ -100,6 +159,11 @@ struct Config LoRaConfig *rx_lora; LoRaConfig *tx_lora; + BusConfig uart0; + BusConfig uart1; + BusConfig spi1; + BusConfig wire1; + bool is_host; bool lora_enabled; @@ -110,6 +174,10 @@ struct Config listen_on_serial0(String("none")), listen_on_serial1(String("readline")), listen_on_usb(String("readline")), rx_lora(configureLora(DEFAULT_RX)), tx_lora(configureLora(DEFAULT_TX)), is_host(DEFAULT_IS_LORA_HOST), + uart0(BusConfig::configure(DEFAULT_UART0)), + uart1(BusConfig::configure(DEFAULT_UART1)), + spi1(BusConfig::configure(DEFAULT_SPI1)), + wire1(BusConfig::configure(DEFAULT_WIRE1)), lora_enabled(DEFAULT_LORA_ENABLED) {}; bool write_config(const char *path); diff --git a/lib/heading/Compass.cpp b/lib/heading/Compass.cpp index 3fa5309..8a86871 100644 --- a/lib/heading/Compass.cpp +++ b/lib/heading/Compass.cpp @@ -1,5 +1,4 @@ #include "heading.h" -#include /* * QMC5883L Registers: @@ -62,11 +61,6 @@ bool QMC5883LCompass::begin() { _lastErr = CompassStatus::COMPASS_UNINITIALIZED; - if (!Wire1.begin(46, 42)) /* SDA, SCL */ - { - return false; - } - String err = selfTest(); if (!err.startsWith("OK\n")) { @@ -77,16 +71,17 @@ bool QMC5883LCompass::begin() return true; } -uint8_t _write_register(uint8_t addr, uint8_t reg, uint8_t value, bool skipValue = false) +uint8_t _write_register(TwoWire &wire, uint8_t addr, uint8_t reg, uint8_t value, + bool skipValue = false) { - Wire1.beginTransmission(addr); - size_t s = Wire1.write(reg); + wire.beginTransmission(addr); + size_t s = wire.write(reg); if (s == 1 && !skipValue) { - s = Wire1.write(value); + s = wire.write(value); } - size_t s1 = Wire1.endTransmission(); + size_t s1 = wire.endTransmission(); if (s != 1 && s1 == 0) { return 1; // "data too long to fit in transmit buffer" @@ -95,30 +90,31 @@ uint8_t _write_register(uint8_t addr, uint8_t reg, uint8_t value, bool skipValue return s1; } -int8_t _read_registers(uint8_t addr, uint8_t reg, uint8_t *v, size_t sz, +int8_t _read_registers(TwoWire &wire, uint8_t addr, uint8_t reg, uint8_t *v, size_t sz, bool skipRegister = false) { if (!skipRegister) { - uint8_t s = _write_register(addr, reg, 0, true); + uint8_t s = _write_register(wire, addr, reg, 0, true); if (s != 0) { return s; } } - uint8_t r = Wire1.requestFrom(addr, sz); + uint8_t r = wire.requestFrom(addr, sz); for (int i = 0; i < r; i++, v++) { - *v = Wire1.read(); + *v = wire.read(); } return r - sz; } -uint8_t _read_register(uint8_t addr, uint8_t reg, uint8_t &v, bool skipRegister = false) +uint8_t _read_register(TwoWire &wire, uint8_t addr, uint8_t reg, uint8_t &v, + bool skipRegister = false) { - uint8_t r = _read_registers(addr, reg, &v, 1, skipRegister); + uint8_t r = _read_registers(wire, addr, reg, &v, 1, skipRegister); if (r != 0) { return 1; @@ -127,10 +123,10 @@ uint8_t _read_register(uint8_t addr, uint8_t reg, uint8_t &v, bool skipRegister return 0; } -int8_t _read_xyz(CompassXYZ &xyz) +int8_t _read_xyz(TwoWire &wire, CompassXYZ &xyz) { xyz.status = 0; - size_t s = _read_register(QMC5883_ADDR, QMC5883_STATUS_REG, xyz.status); + size_t s = _read_register(wire, QMC5883_ADDR, QMC5883_STATUS_REG, xyz.status); if (s != 0) { return s; @@ -139,7 +135,7 @@ int8_t _read_xyz(CompassXYZ &xyz) if ((xyz.status & QMC5883_STATUS_DRDY) == 0) { delay(10); - s = _read_register(QMC5883_ADDR, QMC5883_STATUS_REG, xyz.status); + s = _read_register(wire, QMC5883_ADDR, QMC5883_STATUS_REG, xyz.status); if (s != 0) { return s; @@ -148,7 +144,7 @@ int8_t _read_xyz(CompassXYZ &xyz) int16_t mags[3]; - int8_t r = _read_registers(QMC5883_ADDR, 0, (uint8_t *)&mags, 6); + int8_t r = _read_registers(wire, QMC5883_ADDR, 0, (uint8_t *)&mags, 6); xyz.x = mags[0]; xyz.y = mags[1]; xyz.z = mags[1]; @@ -160,8 +156,8 @@ uint8_t QMC5883LCompass::setMode(CompassMode m) { if (m == CompassMode::COMPASS_IDLE) { - uint8_t s = _write_register(QMC5883_ADDR, QMC5883_FBR_REG, 0); - s |= _write_register(QMC5883_ADDR, QMC5883_CTR_REG, 0); + uint8_t s = _write_register(wire, QMC5883_ADDR, QMC5883_FBR_REG, 0); + s |= _write_register(wire, QMC5883_ADDR, QMC5883_CTR_REG, 0); return s; } @@ -185,13 +181,14 @@ uint8_t QMC5883LCompass::setMode(CompassMode m) return 1; } - uint8_t s = _write_register(QMC5883_ADDR, QMC5883_FBR_REG, 1); // set/reset period + uint8_t s = + _write_register(wire, QMC5883_ADDR, QMC5883_FBR_REG, 1); // set/reset period if (s != 0) { return s; } - return _write_register(QMC5883_ADDR, QMC5883_CTR_REG, + return _write_register(wire, QMC5883_ADDR, QMC5883_CTR_REG, (osr << 6) | (rng << 4) | (odr << 2) | mode); } @@ -211,7 +208,7 @@ String QMC5883LCompass::selfTest() for (int i = 0; i < 100; i++) { CompassXYZ xyz; - int8_t r = _read_xyz(xyz); + int8_t r = _read_xyz(wire, xyz); if (r < 0) { errors = true; @@ -238,7 +235,7 @@ String QMC5883LCompass::selfTest() return res; } -int8_t QMC5883LCompass::readXYZ() { return _read_xyz(xyz); } +int8_t QMC5883LCompass::readXYZ() { return _read_xyz(wire, xyz); } int64_t Compass::lastRead() { diff --git a/lib/heading/heading.h b/lib/heading/heading.h index d3bebbb..193faca 100644 --- a/lib/heading/heading.h +++ b/lib/heading/heading.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include struct HeadingSensor @@ -57,7 +58,8 @@ struct Compass : HeadingSensor struct QMC5883LCompass : Compass { - QMC5883LCompass() : Compass() {} + TwoWire &wire; + QMC5883LCompass(TwoWire &wire) : Compass(), wire(wire) {} bool begin() override; String selfTest() override; diff --git a/lib/loraboards/LiLyGo.h b/lib/loraboards/LiLyGo.h index 103240b..2be621b 100644 --- a/lib/loraboards/LiLyGo.h +++ b/lib/loraboards/LiLyGo.h @@ -18,10 +18,10 @@ #ifndef HELTEC_NO_RADIO_INSTANCE #ifndef ARDUINO_heltec_wifi_32_lora_V3 // Assume MISO and MOSI being wrong when not using Heltec's board definition -// and use hspi to make it work anyway. See heltec_setup() for the actual SPI setup. +// and use vspi to make it work anyway. See heltec_setup() for the actual SPI setup. #include -extern SPIClass *hspi; -#define RADIO_MODULE_INIT() new Module(SS, DIO1, RST_LoRa, BUSY_LoRa, *hspi); +extern SPIClass *vspi; +#define RADIO_MODULE_INIT() new Module(SS, DIO1, RST_LoRa, BUSY_LoRa, *vspi); #else // ARDUINO_heltec_wifi_32_lora_V3 #endif // end ARDUINO_heltec_wifi_32_lora_V3 #endif // end HELTEC_NO_RADIO_INSTANCE diff --git a/lib/loraboards/LoRaBoards.cpp b/lib/loraboards/LoRaBoards.cpp index 7353c99..9af8543 100644 --- a/lib/loraboards/LoRaBoards.cpp +++ b/lib/loraboards/LoRaBoards.cpp @@ -77,7 +77,7 @@ void heltec_setup() #ifdef HELTEC #ifndef ARDUINO_heltec_wifi_32_lora_V3 - hspi->begin(SCK, MISO, MOSI, SS); + vspi->begin(SCK, MISO, MOSI, SS); #endif #endif #ifndef HELTEC_NO_DISPLAY_INSTANCE @@ -90,7 +90,7 @@ void heltec_setup() #ifdef HELTEC #ifndef ARDUINO_heltec_wifi_32_lora_V3 -SPIClass hspi = new SPIClass(2); +SPIClass vspi = new SPIClass(2); #endif #endif diff --git a/src/main.cpp b/src/main.cpp index 5e69d34..54597ba 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -139,6 +139,7 @@ void sendBTData(float heading, float rssi) #include #endif // end LILYGO +#include #include DroneHeading droneHeading; @@ -1413,6 +1414,31 @@ void setup(void) wf_start = millis(); config = Config::init(); +#if defined(HAS_SDCARD) + SD.end(); + SDCardSPI.end(); // end SPI before other uses, eg radio2 over SPI +#endif + + pinMode(LED, OUTPUT); + pinMode(BUZZER_PIN, OUTPUT); + pinMode(REB_PIN, OUTPUT); + heltec_setup(); + + if (!initUARTs(config)) + { + Serial.println("Failed to initialize UARTs"); + } + + if (!initSPIs(config)) + { + Serial.println("Failed to initialize SPIs"); + } + + if (!initWires(config)) + { + Serial.println("Failed to initialize I2Cs"); + } + r.comms_initialized = Comms::initComms(config); if (r.comms_initialized) { @@ -1423,11 +1449,6 @@ void setup(void) Serial.println("Comms did not initialize"); } - pinMode(LED, OUTPUT); - pinMode(BUZZER_PIN, OUTPUT); - pinMode(REB_PIN, OUTPUT); - heltec_setup(); - #ifdef JOYSTICK_ENABLED calibrate_joy(); pinMode(JOY_BTN_PIN, INPUT_PULLUP); @@ -1653,20 +1674,27 @@ void setup(void) #endif - compass = new QMC5883LCompass(); - if (!compass->begin()) + if (wireDevices & QMC5883L || wire1Devices & QMC5883L) { - Serial.println("Failed to initialize Compass"); + compass = new QMC5883LCompass(wireDevices & QMC5883L ? Wire : Wire1); } - String err = compass->selfTest(); - if (err.startsWith("OK\n")) + if (compass) { - Serial.printf("Compass self-test passed: %s\n", err.c_str()); - } - else - { - Serial.printf("Compass self-sets failed: %s\n", err.c_str()); + if (!compass->begin()) + { + Serial.println("Failed to initialize Compass"); + } + + String err = compass->selfTest(); + if (err.startsWith("OK\n")) + { + Serial.printf("Compass self-test passed: %s\n", err.c_str()); + } + else + { + Serial.printf("Compass self-sets failed: %s\n", err.c_str()); + } } #ifdef UPTIME_CLOCK