diff --git a/.github/workflows/arduino.yml b/.github/workflows/arduino.yml index 5dafd18..d56a0c8 100644 --- a/.github/workflows/arduino.yml +++ b/.github/workflows/arduino.yml @@ -18,6 +18,7 @@ jobs: matrix: board: - adafruit:samd:adafruit_feather_m0 + - esp32:esp32:esp32 runs-on: ubuntu-latest steps: @@ -27,7 +28,7 @@ jobs: - name: Install arduino-cli run: | make setup + pip install pyserial - name: Build run: | - make setup make all TARGET=${{ matrix.board }} diff --git a/.github/workflows/platformio.yml b/.github/workflows/platformio.yml index 52cab33..90c889d 100644 --- a/.github/workflows/platformio.yml +++ b/.github/workflows/platformio.yml @@ -17,6 +17,7 @@ jobs: fail-fast: false matrix: board: + - esp32dev - adafruit_feather_m0 - teensylc diff --git a/examples/PanoController/PanoController.ino b/examples/PanoController/PanoController.ino index 812ba4d..33a9992 100644 --- a/examples/PanoController/PanoController.ino +++ b/examples/PanoController/PanoController.ino @@ -14,11 +14,16 @@ #include "camera.h" #include "mpu.h" #include "battery.h" -#include "bluetooth.h" #include "gcode.h" +#if defined(BLUEFRUIT_SPI_CS) +#include "ble_bluefruit_spi.h" static Adafruit_BluefruitLE_SPI ble(BLUEFRUIT_SPI_CS, BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST); static Bluetooth bluetooth(ble); +#elif defined(ESP32) +#include "ble_esp32.h" +static Bluetooth ble; +#endif static Battery battery(BATTERY, BATT_R1, BATT_R2, VCC); static Camera camera(CAMERA_FOCUS, CAMERA_SHUTTER); @@ -38,24 +43,20 @@ void setup() { digitalWrite(MPU_VCC, HIGH); Serial.println("Configuring stepper drivers"); + horiz_motor.begin(MOTOR_RPM, MICROSTEPS); vert_motor.begin(MOTOR_RPM/2, MICROSTEPS); motors.disable(); // turn off motors at startup Serial.print("Horiz RPM="); Serial.println(horiz_motor.getRPM()); Serial.print("Vert RPM="); Serial.println(vert_motor.getRPM()); +#if !defined(ESP32) Serial.println("Configuring Bluefruit LE"); - ble.begin(true); - ble.echo(false); // disable command echo - ble.factoryReset(); - ble.info(); - ble.verbose(false); // turn off debug info - - Serial.println("Initializing BLE App Communication"); bluetooth.begin(); - - ble.sendCommandCheckOK("AT+BLEBATTEN=1"); // enable battery service - // ble.sendCommandCheckOK("AT+BLEPOWERLEVEL=0"); // can be used to change transmit power +#else + Serial.println("Starting Bluetooth"); + ble.begin("PanoController"); +#endif Serial.print("Checking battery voltage... "); battery.begin(); @@ -71,7 +72,6 @@ void setup() { gcode.setMaxAccel(MOTOR_ACCEL, MOTOR_DECEL); Serial.println("System ready."); - while (!ble.isConnected()){ delay(1000); } @@ -83,7 +83,7 @@ void loop() { static int len; if (ble.available()){ - len = ble.readline(buffer, GCODE_BUF_SIZE); + len = ble.readBytesUntil('\n', buffer, GCODE_BUF_SIZE); *(buffer+len) = '\0'; Serial.print("BLE> "); diff --git a/examples/PanoController/config.h b/examples/PanoController/config.h index 02f35b0..39cdf5a 100644 --- a/examples/PanoController/config.h +++ b/examples/PanoController/config.h @@ -33,6 +33,9 @@ #if defined(ARDUINO_SAMD_FEATHER_M0) || defined(ADAFRUIT_FEATHER_M0) #include "config_feather_m0.h" +#elif defined(ESP32) +#include "config_esp32.h" + #elif defined(__arm__) && defined(CORE_TEENSY) #include "config_teensy.h" diff --git a/examples/PanoController/config_esp32.h b/examples/PanoController/config_esp32.h new file mode 100644 index 0000000..be560fb --- /dev/null +++ b/examples/PanoController/config_esp32.h @@ -0,0 +1,39 @@ +/* + * Pano Controller Configuration File for ESP32 board + * https://github.com/espressif/arduino-esp32/blob/master/docs/esp32_pinmap.png + * https://desire.giesecke.tk/index.php/2018/07/06/reserved-gpios/ + */ +#include + +/* SPI (for display) +SPI2 and SPI3 are general purpose SPI controllers, sometimes referred to as HSPI and VSPI +SDA = IO 23 +SCLK = IO 18 +D/C = IO 21 +RST = IO 22 +CS = IO 5 +*/ + +// Camera shutter controls +#define CAMERA_FOCUS GPIO_NUM_16 +#define CAMERA_SHUTTER GPIO_NUM_17 + +// Battery measurement pin R1/R2 +#define BATTERY GPIO_NUM_34 //34 # ADC1 CH0 + +// MPU (accel/gyro) +// GPIO_NUM_21 I2C SDA +// GPIO_NUM_22 I2C SCL +#define MPU_INT GPIO_NUM_2 +#define MPU_VCC GPIO_NUM_4 + +// Future devices +//#define COMPASS_DRDY xx + +// Stepper drivers control +#define DIR GPIO_NUM_27 +#define VERT_STEP GPIO_NUM_25 +#define HORIZ_STEP GPIO_NUM_26 + +// this should be hooked up to nENABLE on both drivers +#define nENABLE GPIO_NUM_14 diff --git a/platformio.ini b/platformio.ini index 437c87a..d8c4036 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,7 +13,7 @@ description = PanoController-Firmware src_dir = examples/PanoController lib_dir = . default_envs = - adafruit_feather_m0 + esp32dev [env] framework = arduino @@ -42,3 +42,17 @@ board = teensylc lib_deps = ${env.lib_deps} featherfly/SoftwareSerial@^1.0 + +[env:esp32dev] +platform = espressif32 +board = esp32dev +#board = esp-wrover-kit +board_build.f_cpu = 80000000L +lib_deps = + ${env.lib_deps} + plerup/EspSoftwareSerial @ ^6.8.5 +build_type = debug +build_flags = + ${env.build_flags} +# -D CORE_DEBUG_LEVEL=5 +# -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG diff --git a/src/bluetooth.cpp b/src/ble_bluefruit_spi.cpp similarity index 79% rename from src/bluetooth.cpp rename to src/ble_bluefruit_spi.cpp index 88a4e9d..7ad0070 100644 --- a/src/bluetooth.cpp +++ b/src/ble_bluefruit_spi.cpp @@ -1,3 +1,4 @@ +#if !defined(ESP32) /* * App Communication over Bluetooth LE Serial * @@ -7,7 +8,7 @@ * A copy of this license has been included with this distribution in the file LICENSE. */ -#include "bluetooth.h" +#include "ble_bluefruit_spi.h" #define UART_BUFFER_SIZE BLE_BUFSIZE @@ -16,6 +17,13 @@ Bluetooth::Bluetooth(Adafruit_BluefruitLE_SPI& ble) {} void Bluetooth::begin(){ + ble.begin(true); + ble.echo(false); // disable command echo + ble.factoryReset(); + ble.info(); + ble.verbose(false); // turn off debug info + ble.sendCommandCheckOK("AT+BLEBATTEN=1"); // enable battery service + // ble.sendCommandCheckOK("AT+BLEPOWERLEVEL=0"); // can be used to change transmit power ble.sendCommandCheckOK("AT+BAUDRATE=921600"); // LED Activity command is only supported from 0.6.6: MODE, BLEUART ble.sendCommandCheckOK("AT+HWMODELED=BLEUART"); @@ -48,3 +56,4 @@ void Bluetooth::poll(uint32_t timeout){ } while (p == eob); // we filled the buffer so there may be more to read } } +#endif // !defined(ESP32) diff --git a/src/bluetooth.h b/src/ble_bluefruit_spi.h similarity index 92% rename from src/bluetooth.h rename to src/ble_bluefruit_spi.h index 4f480db..a69b8c1 100644 --- a/src/bluetooth.h +++ b/src/ble_bluefruit_spi.h @@ -1,3 +1,4 @@ +#if !defined(ESP32) /* * App Communication over Bluetooth LE Serial * @@ -23,3 +24,4 @@ class Bluetooth { }; #endif /* BLUETOOTH_H_ */ +#endif // !defined(ESP32) diff --git a/src/ble_esp32.cpp b/src/ble_esp32.cpp new file mode 100644 index 0000000..28ffec0 --- /dev/null +++ b/src/ble_esp32.cpp @@ -0,0 +1,109 @@ +#if defined(ESP32) + +#include "ble_esp32.h" + +// Nordic UART uuids +#define UART_SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" +#define UART_RX_CHAR_UUID "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" +#define UART_TX_CHAR_UUID "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" +#define PANO_SERVICE_UUID (uint16_t)2017 + +// How many bytes we can send at once +#define BLOCK_SIZE 128 + +void Bluetooth::begin(const char *name) { + BLEDevice::init(name); + + pServer = BLEDevice::createServer(); + pServer->setCallbacks(&serverCallbacks); + + pServer->createService(PANO_SERVICE_UUID)->start(); + + BLEService *pService = pServer->createService(UART_SERVICE_UUID); + rx = pService->createCharacteristic(UART_RX_CHAR_UUID, BLECharacteristic::PROPERTY_WRITE); + rx->setCallbacks(&rxCallbacks); + + tx = pService->createCharacteristic(UART_TX_CHAR_UUID, BLECharacteristic::PROPERTY_NOTIFY); + tx->addDescriptor(new BLE2902()); // 0x2902: org.bluetooth.descriptor.gatt.client_characteristic_configuration + + pService->start(); + + pServer->startAdvertising(); +} + +bool Bluetooth::isConnected(void) { + return ((ServerCallbacks *)&serverCallbacks)->isConnected(); // FIXME: temporary +} + +void RXCallbacks::onWrite(BLECharacteristic *pCharacteristic) { + std::string val = pCharacteristic->getValue(); + int len = val.length(); + if (len > 0 && abs(writeAt - readAt) < len ) { + if (writeAt + len < buf + FIFO_SIZE) { // no wrap + std::copy(val.begin(), val.end(), writeAt); + writeAt += len; + } else { + int wrapLen = buf + FIFO_SIZE - writeAt; + std::copy(val.begin(), val.begin() + wrapLen, writeAt); + std::copy(val.begin() + wrapLen, val.end(), buf); + writeAt = buf + len - wrapLen; + } + } +}; + +int RXCallbacks::read(void) { + return available() ? (int)(*readAt++) : EOF; +} + +int RXCallbacks::peek(void) { + return available() ? (int)(*readAt) : EOF; +} + +void RXCallbacks::flush(void) { + writeAt = readAt = buf; +} + +/* + * Implement Stream interface + */ +size_t Bluetooth::write(uint8_t c) { + if (isConnected()) { + tx->setValue(&c, 1); + tx->notify(); + ets_delay_us(10000); // bluetooth stack will go into congestion, if too many packets are sent + return 1; + } + return 0; +}; + +size_t Bluetooth::write(const uint8_t *buf, size_t size) { + size_t remain = size; + while (isConnected() && remain > 0) { + if (remain > BLOCK_SIZE){ + tx->setValue((uint8_t*)buf, BLOCK_SIZE); + remain -= BLOCK_SIZE; + buf += BLOCK_SIZE; + } else { + tx->setValue((uint8_t*)buf, remain); + remain = 0; + } + tx->notify(); + ets_delay_us(10000); + } + return size - remain; +} + +int Bluetooth::available(void) { + return rxCallbacks.available(); +}; +int Bluetooth::read(void) { + return rxCallbacks.read(); +}; +int Bluetooth::peek(void) { + return rxCallbacks.peek(); +}; +void Bluetooth::flush(void) { + rxCallbacks.flush(); +}; + +#endif // defined(ESP32) diff --git a/src/ble_esp32.h b/src/ble_esp32.h new file mode 100644 index 0000000..9be337d --- /dev/null +++ b/src/ble_esp32.h @@ -0,0 +1,85 @@ +#if defined(ESP32) + +#ifndef BLE_ESP32_H +#define BLE_ESP32_H + +#include +#include +#include +#include +#include + +#define FIFO_SIZE 256 + +class ServerCallbacks : public BLEServerCallbacks { +private: + volatile bool deviceConnected; + +public: + void onConnect(BLEServer *pServer) { + deviceConnected = true; + }; + + void onDisconnect(BLEServer *pServer) { + deviceConnected = false; + pServer->startAdvertising(); + }; + + bool isConnected(void) { + return deviceConnected; + } +}; + +class RXCallbacks : public BLECharacteristicCallbacks { +private: + uint8_t buf[FIFO_SIZE]; + uint8_t *readAt; + uint8_t *writeAt; +public: + RXCallbacks() + { + flush(); + } + void onWrite(BLECharacteristic *pCharacteristic); + + bool available(void) { + return readAt != writeAt; + } + // Class Stream interface + int read(void); + int peek(void); + void flush(void); + +}; + + +class Bluetooth : public Stream { +private: + BLEServer *pServer; + BLECharacteristic *tx, *rx; + ServerCallbacks serverCallbacks; + RXCallbacks rxCallbacks; + uint8_t txValue = 0; +public: + Bluetooth() {}; + void begin(const char *name); + bool isConnected(); + + + // Class Print virtual function Interface + virtual size_t write(uint8_t c); + virtual size_t write(const uint8_t *buffer, size_t size); + + // pull in write(str) and write(buf, size) from Print + using Print::write; + + // Class Stream interface + virtual int available(void); + virtual int read(void); + virtual void flush(void); + virtual int peek(void); +}; + +#endif /* BLE_ESP32_H */ + +#endif // defined(ESP32) diff --git a/src/gcode.cpp b/src/gcode.cpp index 9c75367..ea41326 100644 --- a/src/gcode.cpp +++ b/src/gcode.cpp @@ -187,6 +187,8 @@ void GCode::execute(char buffer[]){ if (cmd.target.a != 0 || cmd.target.c != 0){ move(cmd.motion, cmd.coords, cmd.current, cmd.target); } + default: // ESP32 environment uses -Werror=switch which would raise an error here + break; } // Data Query. These are here so we can read the final state of a move. @@ -259,6 +261,8 @@ void GCode::execute(char buffer[]){ case Wait::M30: dln("END"); break; + default: // ESP32 environment uses -Werror=switch which would raise an error here + break; } }