diff --git a/README.md b/README.md index 1c3011c..63f3965 100644 --- a/README.md +++ b/README.md @@ -23,58 +23,83 @@ Current state: fully functional prototype board, field-tested, frequently upgrad - Grid size and position - Precision movement control - Multiple delay options: pre-shutter and post-shutter, short or long shutter pulse (for bracketing). +- Bluetooth joystick control via phone ### Hardware - 32-bit ARM controller board - OLED 128x64 display -- Joystick menu navigation and optional IR remote +- Joystick menu navigation, optional IR remote - Can operate with battery voltage from 10V down to 6V - Lower power usage and later voltage cutoff than original Gigapan ## Wiring map -Breadboard setup with Teensy LC - ### Teensy LC / 3.x - A0 - Battery Voltage via divider: Vin---[47K]---A0---[10K]---GND -- A1 -- A2 - Joystick Vx -- A3 - Joystick Vy +- A1 - Joystick SW +- A2 - Joystick Vy +- A3 - Joystick Vx - A4 - SDA - Display and MPU-6050 board - A5 - SCL - Display and MPU-6050 board - A6 - A7 - D0/RX - camera focus (active LOW) - D1/TX - camera shutter (active LOW) -- D2(int) - Joystick SW +- D2 - MPU-6050 INT - D3(int) - IR Remote In (AX-1838HS) -- D4 -- D5 - StepperV DIR +- D4 - BLE RST +- D5 - DIR (both) - D6 - StepperV STEP -- D7 - MPU-6050 INT -- D8 - StepperH DIR +- D7 - BLE INT +- D8 - BLE CS - D9 - StepperH STEP -- D10- M0 -- D11- M1 -- D12 -- D13(led) - ~SLEEP (to both steppers) - LED indicates motors are on +- D10 - ~ENABLE (both) +- D11 - SPI MOSI[BLE] +- D12 - SPI MISO[BLE] +- D13[LED] - SPI SCK[BLE] + +### Feather M0 / Bluefruit + +- A0 +- A1 - Joystick SW +- A2 - Joystick Vy +- A3 - Joystick Vx +- A4 - Battery Voltage via divider: Vin---[47K]---A0---[10K]---GND +- A5 +- SCK[BLE] +- MOSI[BLE] +- MISO[BLE] +- RX/0 - camera focus (active LOW) +- TX/1 - camera shutter (active LOW) + +- 4[BLE] CS (internally connected) +- 7[BLE] IRQ (internally connected) +- 8[BLE] RST (internally connected) + +- SDA/20 - Display and MPU-6050 board +- SCL/21 - Display and MPU-6050 board +- 5 - DIR (both) +- 6 - StepperV STEP +- 9[A7] - StepperH STEP +- 10 - ~ENABLE (both) +- 11 +- 12 - MPU-6050 INT +- 13[LED] ### Other -- All ~EN tied to GND +- All ~SLEEP tied to Vcc - All VMOT tied to Vin +- All M1 tied to Vcc (1:32 mode) +- All M0 left unconnected (1:32 mode) - 3.3V step-down adapter from Vin to Vcc ## Notes - *Atmega328-based boards are not supported*, see issue #57 - IR remote not supported on Adafruit Feather M0, see issue #59 -- Settings memory not supported on Feather M0 (no EEPROM) +- Settings memory on Feather M0 only works with Bluefruit (M0 has no EEPROM itself) - Future rewiring plan - - M0, M1 can be hardwired (M0=Vcc, M1 unconnected for 1:32 mode) - - DIR should be shared (it is only sampled on STEP) - - Tie all ~EN together to ground - - Tie all ~SLEEP together to D13 (LED indicates motors are on) - if we ever want to use ESP-12, need to reduce pins. ESP-12 only has 11: (0,2,4,5,12,13,14,15,16,RXD,TXD,ADC) - Adafruit Feather M0 BLE uses 4, 7, 8 as Bluefruit RESET, IRQ and CS. @@ -111,6 +136,7 @@ lower current even, if we reduce the speed. - (untested but same Cortex M0 as above) Arduino Zero - 2 x DRV8834 Low-Voltage Stepper Motor Driver from Pololu - 128x64 OLED display, SSD1306 I2C from anywhere +- Adafruit Bluefruit SPI Breakout (if not onboard) - provides Bluetooth LE 4.1 joystick control - optional - 2-axis + switch analog joystick - 1834HS IR receiver with some remote - optional but recommended - Remote codes are hardcoded in remote.cpp if you have a different remote @@ -127,6 +153,7 @@ lower current even, if we reduce the speed. ### Libraries - Adafruit_SSD1306 - Adafruit_GFX +- Adafruit Bluefruit nRF51 - IRremote - Wire - StepperDriver @@ -145,3 +172,4 @@ The only thing required of the platform is the two horiz/vert stepper motors. - Notes: - the DRV8834 current limit must be set according to motor spec - reduction gear settings are hardcoded in pano.h + diff --git a/WIRING.md b/WIRING.md deleted file mode 100644 index 296d55a..0000000 --- a/WIRING.md +++ /dev/null @@ -1,55 +0,0 @@ - -# Wiring Diagrams - -## Hardware - -### Initial Setup - -- Arduino Pro Mini (5V/16Mhz) -- SSD1306 I2C Oled Display 128x64 -- Joystick (VRx, VRy, SW) -- 2 x - - DRV8834 - - Stepper gigapan's 39BYG001 (5V,1A,1.8Kg-cm) or 39BYG101 (5V,0.4A,0.66Kg-cm) - - 100uF cap -- 7.2V - 10.8V batter or power supply - - 2.5A for gigapan - - 1.25A for the other -- Camera shutter cable (GND/Focus/Shutter) - -### Expansion -- MPU-6050 -- HMC6883L - -### Pro-Mini pin allocation - -A0 -A1 -A2 - Joystick Vx -A3 - Joystick Vy -A4 - SDA - SSD1306, MPU6050, HMC6883L -A5 - SCL - SSD1306, MPU6050, HMC6883L -A6 (needs pin) -A7 (needs pin) - -D0/RX -D1/TX -D2(int) - Joystick SW -D3(int) -D4 - StepperV ~EN -D5 - StepperV DIR -D6 - StepperV STEP -D7 - StepperH ~EN -D8 - StepperH DIR -D9 - StepperH STEP -D10- M0 (unless we preset stepping mode) -D11- M1 (unless we preset stepping mode) -D12- camera focus -D13(led) - camera shutter - -## Wiring Diagram - -DRV8834: -- M0 - GND (1:1 step) -- M1 - GND (1:1 step) -- SLEEP - Vcc diff --git a/ble_remote.cpp b/ble_remote.cpp new file mode 100644 index 0000000..a6430a1 --- /dev/null +++ b/ble_remote.cpp @@ -0,0 +1,111 @@ +/* + * Bluetooth LE Remote + * + * Copyright (C)2016 Laurentiu Badea + * + * This file may be redistributed under the terms of the MIT license. + * A copy of this license has been included with this distribution in the file LICENSE. + */ + +#include "ble_remote.h" + +#define PACKET_BUTTON_LEN (5) +#define READ_BUFSIZE (20) + +#define BUTTON_ID(code) (code & 0x0f) +#define IS_PRESSED(code) (code >> 4) + +BLERemote::BLERemote(Adafruit_BluefruitLE_SPI& ble) +:ble(ble) +{} + +void BLERemote::init(void){ + if (ble.isConnected()){ + if (!active){ + ble.setMode(BLUEFRUIT_MODE_DATA); + active = true; + Serial.println("Connected"); + } + } else { + active = false; + } +} + +unsigned BLERemote::read(void){ + unsigned event = EVENT_NONE; + + uint8_t buttonCode = readButtonCode(); + + if (IS_PRESSED(buttonCode)){ + switch (BUTTON_ID(buttonCode)){ + case 1: + event |= EVENT_OK; + break; + case 3: + event |= EVENT_CANCEL; + break; + case 5: + event |= EVENT_UP; + break; + case 6: + event |= EVENT_DOWN; + break; + case 7: + event |= EVENT_LEFT; + break; + case 8: + event |= EVENT_RIGHT; + break; + } + + next_repeat_time = millis() + REPEAT_DELAY; + last_event = event; + + } else if (BUTTON_ID(buttonCode)){ // button depressed + last_event = EVENT_NONE; + + } else if (last_event){ // button currently remaining pressed + if (millis() > next_repeat_time && !isEventOk(last_event) && !isEventCancel(last_event)){ + event = last_event; + next_repeat_time = millis() + REPEAT_INTERVAL; + } + } + return event; +} + +/* + * Read the controller messages. + * Returns a byte which contains button number in low nibble, and pressed state in the high. + */ +uint8_t BLERemote::readButtonCode(void){ + static uint8_t packetbuffer[READ_BUFSIZE]; + + init(); + if (!active){ + return 0; + } + + int i = 0; + while (ble.available() && i < PACKET_BUTTON_LEN){ + char c = ble.read(); + if (i == 0 and c != '!'){ + continue; + } + packetbuffer[i++] = c; + } + + if (!i || packetbuffer[1] != 'B'){ + return 0; + } + + // Verify checksum + uint8_t checksum = 1; + for (; i > 0;){ + checksum += packetbuffer[--i]; + } + if (checksum){ // checksum mismatch + return 0; + } + + return (packetbuffer[2] - '0') + (packetbuffer[3] - '0' << 4); +} diff --git a/ble_remote.h b/ble_remote.h new file mode 100644 index 0000000..19f4c98 --- /dev/null +++ b/ble_remote.h @@ -0,0 +1,29 @@ +/* + * Bluetooth LE Remote + * + * Copyright (C)2016 Laurentiu Badea + * + * This file may be redistributed under the terms of the MIT license. + * A copy of this license has been included with this distribution in the file LICENSE. + */ +#ifndef BLE_REMOTE_H_ +#define BLE_REMOTE_H_ + +#include +#include "hid.h" + +class BLERemote : public HID { +private: + Adafruit_BluefruitLE_SPI& ble; + uint8_t readButtonCode(void); + unsigned last_event = EVENT_NONE; + unsigned next_repeat_time; + +public: + bool active = false; + BLERemote(Adafruit_BluefruitLE_SPI& ble); + void init(void); + unsigned read(void) override; +}; + +#endif /* BLE_REMOTE_H_ */ diff --git a/config.h b/config.h new file mode 100644 index 0000000..fb6c536 --- /dev/null +++ b/config.h @@ -0,0 +1,36 @@ +/* + * Pano Controller Master Configuration File + */ + +#if defined(__AVR__) +#error "AVR is not supported" + +#elif !defined(ARDUINO_SAMD_FEATHER_M0) +#include "config_teensy.h" + +#else +#include "config_feather_m0.h" +#endif + +// Address of I2C OLED display. If screen looks scaled edit Adafruit_SSD1306.h +// and pick SSD1306_128_64 or SSD1306_128_32 that matches display type. +#define DISPLAY_I2C_ADDRESS 0x3C +#define OLED_RESET 12 +#define TEXT_SIZE 1 +#define DISPLAY_ROWS SSD1306_LCDHEIGHT/8/TEXT_SIZE +#define DISPLAY_COLS SSD1306_LCDWIDTH/6/TEXT_SIZE + +// Battery monitoring settings +#define VCC 3300 +#define LOW_BATTERY 7000 +// R1/R2 is the voltage divisor in Ω (GND-R1-A0-R2-Vin) +// measure resistors and enter actual values for a more accurate voltage +#define BATT_R1 9980 +#define BATT_R2 46500 +#define BATT_RANGE (VCC * (BATT_R1 + BATT_R2) / BATT_R1) + +// MPU (accel/gyro) +#define MPU_I2C_ADDRESS 0x68 + +// Stepper motors steps per revolution +#define MOTOR_STEPS 200 diff --git a/config_feather_m0.h b/config_feather_m0.h index de7a401..1d23e70 100644 --- a/config_feather_m0.h +++ b/config_feather_m0.h @@ -5,52 +5,37 @@ * - Adafruit Feather M0 Bluefruit */ -// Address of I2C OLED display. If screen looks scaled edit Adafruit_SSD1306.h -// and pick SSD1306_128_64 or SSD1306_128_32 that matches display type. -#define DISPLAY_I2C_ADDRESS 0x3C -#define OLED_RESET 12 -#define TEXT_SIZE 1 -#define DISPLAY_ROWS SSD1306_LCDHEIGHT/8/TEXT_SIZE -#define DISPLAY_COLS SSD1306_LCDWIDTH/6/TEXT_SIZE +// Bluetooth module. These internally connected on the Feather Bluefruit. +#define BLUEFRUIT_SPI_CS 8 +#define BLUEFRUIT_SPI_IRQ 7 +#define BLUEFRUIT_SPI_RST 4 // Camera shutter controls #define CAMERA_FOCUS 0 #define CAMERA_SHUTTER 1 -// Battery measurement settings -#define VCC 3300 -#define LOW_BATTERY 7000 -// R1/R2 is the voltage divisor in Ω (GND-R1-A0-R2-Vin) -// measure resistors and enter actual values for a more accurate voltage -#define BATT_R1 9980 -#define BATT_R2 46500 -#define BATT_RANGE (VCC * (BATT_R1 + BATT_R2) / BATT_R1) -#define BATTERY A1 +// Battery measurement pin R1/R2 +#define BATTERY A4 // Joystick inputs -#define JOYSTICK_X A2 -#define JOYSTICK_Y A3 -#define JOYSTICK_SW A4 +#define JOYSTICK_X A3 +#define JOYSTICK_Y A2 +#define JOYSTICK_SW A1 // IR remote is not supported on Feather M0 // https://github.com/z3t0/Arduino-IRremote/issues/274 #define REMOTE_IN -1 // MPU (accel/gyro) -#define MPU_I2C_ADDRESS 0x68 #define MPU_INT 12 // Future devices -#define COMPASS_DRDY 11 +//#define COMPASS_DRDY 11 -// Stepper motors and drivers -#define MOTOR_STEPS 200 -#define VERT_DIR 5 +// Stepper drivers control +#define DIR 5 #define VERT_STEP 6 -#define HORIZ_DIR VERT_DIR #define HORIZ_STEP 9 -#define DRV_M0 10 -#define DRV_M1 11 -// this should be hooked up to nSLEEP on both drivers -#define MOTORS_ON 13 +// this should be hooked up to nENABLE on both drivers +#define nENABLE 10 diff --git a/config_teensy.h b/config_teensy.h index 37cbd9a..3b60f59 100644 --- a/config_teensy.h +++ b/config_teensy.h @@ -6,51 +6,40 @@ * - Teensy 3.2 */ -// Address of I2C OLED display. If screen looks scaled edit Adafruit_SSD1306.h -// and pick SSD1306_128_64 or SSD1306_128_32 that matches display type. -#define DISPLAY_I2C_ADDRESS 0x3C -#define OLED_RESET 12 -#define TEXT_SIZE 1 -#define DISPLAY_ROWS SSD1306_LCDHEIGHT/8/TEXT_SIZE -#define DISPLAY_COLS SSD1306_LCDWIDTH/6/TEXT_SIZE +// Bluetooth module SPI +#define BLUEFRUIT_SPI_CS 8 +#define BLUEFRUIT_SPI_IRQ 7 +#define BLUEFRUIT_SPI_RST 4 // Optional but recommended, set to -1 if unused + +#define SPI_MOSI 11 +#define SPI_MISO 12 +#define SPI_SCK 13 // Camera shutter controls #define CAMERA_FOCUS 0 #define CAMERA_SHUTTER 1 -// Battery measurement settings -#define VCC 3300 -#define LOW_BATTERY 7000 -// R1/R2 is the voltage divisor in Ω (GND-R1-A0-R2-Vin) -// measure resistors and enter actual values for a more accurate voltage -#define BATT_R1 9980 -#define BATT_R2 46500 -#define BATT_RANGE (VCC * (BATT_R1 + BATT_R2) / BATT_R1) +// Battery measurement pin R1/R2 #define BATTERY A0 // Joystick inputs #define JOYSTICK_X A3 #define JOYSTICK_Y A2 -#define JOYSTICK_SW 2 +#define JOYSTICK_SW A1 // IR remote #define REMOTE_IN 3 // MPU (accel/gyro) -#define MPU_I2C_ADDRESS 0x68 -#define MPU_INT 7 +#define MPU_INT 2 // Future devices -#define COMPASS_DRDY 4 +//#define COMPASS_DRDY 4 -// Stepper motors and drivers -#define MOTOR_STEPS 200 -#define VERT_DIR 5 +// Stepper drivers control +#define DIR 5 #define VERT_STEP 6 -#define HORIZ_DIR 8 #define HORIZ_STEP 9 -#define DRV_M0 10 -#define DRV_M1 11 -// this should be hooked up to nSLEEP on both drivers -#define MOTORS_ON 13 +// this should be hooked up to nENABLE on both drivers +#define nENABLE 10 diff --git a/eeprom.h b/eeprom.h index 9fc4891..9c340d2 100644 --- a/eeprom.h +++ b/eeprom.h @@ -13,13 +13,28 @@ #include #else +#include + +extern Adafruit_BluefruitLE_SPI ble; + class { // fake EEPROM public: int read(int addr){ return 0; }; void write(int addr, unsigned char c){ }; - int get(int addr, int default_value){ return default_value; }; - void put(int addr, int value){ }; + int get(int addr, int& value){ + int32_t v; + ble.readNVM(addr, &v); + value = v; + return value; + }; + void put(int addr, int value){ + int old_value; + get(addr, old_value); + if (old_value != value){ + ble.writeNVM(addr, value); + } + }; } EEPROM; #endif diff --git a/hid.h b/hid.h index 7ffc88a..d4d4a2c 100644 --- a/hid.h +++ b/hid.h @@ -11,6 +11,11 @@ #include +// wait at least this many ms befor auto repeat +#define REPEAT_DELAY 500 +// auto repeat interval (ms) +#define REPEAT_INTERVAL 150 + class HID { protected: enum Event { diff --git a/images/breadboard.jpg b/images/breadboard.jpg index b53c3b8..f1d6260 100644 Binary files a/images/breadboard.jpg and b/images/breadboard.jpg differ diff --git a/images/connection-diagram.png b/images/connection-diagram.png deleted file mode 100644 index 2cf7dfb..0000000 Binary files a/images/connection-diagram.png and /dev/null differ diff --git a/joystick.cpp b/joystick.cpp index 78f3abd..d08f76c 100644 --- a/joystick.cpp +++ b/joystick.cpp @@ -38,6 +38,17 @@ Joystick::~Joystick(void){ detachInterrupt(digitalPinToInterrupt(sw_pin)); } +bool Joystick::isConnected(void){ + // if joystick is not center at start, it is not connected + if (!connected){ + if (getPositionX(false) == 0 && getPositionY(false) == 0){ + connected || Serial.println("Joystick connected."); + connected = true; + } + } + return connected; +} + unsigned Joystick::read(void){ unsigned event = EVENT_NONE; int current_state; @@ -46,6 +57,10 @@ unsigned Joystick::read(void){ return EVENT_NONE; } + if (!isConnected()){ + return event; + } + // read click switch current_state = (button_clicked) ? LOW : digitalRead(sw_pin); if (current_state != sw_state){ @@ -81,11 +96,17 @@ unsigned Joystick::read(void){ return event; } -int Joystick::getPositionX(void){ +int Joystick::getPositionX(bool if_connected){ + if (if_connected && !isConnected()){ + return 0; + } return ((int)analogRead(x_pin)-512+(1<<(sensitivity-1))) >> sensitivity; } -int Joystick::getPositionY(void){ +int Joystick::getPositionY(bool if_connected){ + if (if_connected && !isConnected()){ + return 0; + } return (512-(int)analogRead(y_pin)+(1<<(sensitivity-1))) >> sensitivity; } diff --git a/joystick.h b/joystick.h index fbdfccc..79c5404 100644 --- a/joystick.h +++ b/joystick.h @@ -18,6 +18,7 @@ class Joystick : public HID { int sw_pin, x_pin, y_pin; int sw_state = 0, x_state = 0, y_state = 0; int last_read; + bool connected = false; public: static const int range = (1024/2) >> sensitivity; int autorepeat_delay; @@ -25,9 +26,10 @@ class Joystick : public HID { unsigned read(void) override; Joystick(int sw_pin, int x_pin, int y_pin); ~Joystick(void); - int getPositionX(void); - int getPositionY(void); + int getPositionX(bool if_connected=true); + int getPositionY(bool if_connected=true); bool getButtonState(void); + bool isConnected(void); }; #endif /* JOYSTICK_H_ */ diff --git a/menu.h b/menu.h index ff40a96..adbaf1e 100644 --- a/menu.h +++ b/menu.h @@ -142,7 +142,7 @@ class Menu : public MultiSelect { int render(DISPLAY_DEVICE display, int rows) override; }; -extern Menu menu; +Menu* getMainMenu(void); void displayMenu(Menu& menu, DISPLAY_DEVICE display, const int rows, AllHID& hid, void(*onMenuLoop)(void)=NULL); diff --git a/menus_definition.cpp b/menus_definition.cpp index b79ab27..dec98d5 100644 --- a/menus_definition.cpp +++ b/menus_definition.cpp @@ -41,48 +41,52 @@ int assignEEPROM(){ } /* - * + * Return the main menu object */ -Menu menu("Main Menu", 5, new BaseMenu* const[5] { +Menu* getMainMenu(void){ + static Menu* menu = + new Menu("Main Menu", 5, new BaseMenu* const[5] { - new Menu("Pano", 6, new BaseMenu* const[6]{ - new ActionItem("New Pano", onStart), - new ActionItem("Repeat Last", onRepeat), - new ActionItem("360 Pano", on360), - new ActionItem("Last Pano Info", onPanoInfo), - new RangeSelector("Manual Horiz FOV", &horiz, 120, USE_EEPROM, NULL, 10, 360, 10), - new RangeSelector("Manual Vert FOV", &vert, 90, USE_EEPROM, NULL, 10, 180, 10) - }), + new Menu("Pano", 6, new BaseMenu* const[6]{ + new ActionItem("New Pano", onStart), + new ActionItem("Repeat Last", onRepeat), + new ActionItem("360 Pano", on360), + new ActionItem("Last Pano Info", onPanoInfo), + new RangeSelector("Manual Horiz FOV", &horiz, 120, USE_EEPROM, NULL, 10, 360, 10), + new RangeSelector("Manual Vert FOV", &vert, 90, USE_EEPROM, NULL, 10, 180, 10) + }), - new Menu("Camera", 8, new BaseMenu* const[8] { - new ListSelector("Focal Len", &focal, 35, USE_EEPROM, NULL, 16, - (const int[]){12, 14, 16, 20, 24, 28, 35, 50, 75, 105, 200, 300, 400, 450, 500, 600}), - new NamedListSelector("Shutter", &shutter, 10, USE_EEPROM, NULL, 11, - (const char * const[]){"1/100", "1/50", "1/25", "1/10", "1/4", "0.5s", "1s", "2s", "4s", "8s", "MANUAL"}, - (const int[]){10, 20, 40, 100, 250, 500, 1000, 2000, 4000, 8000, 0}), - new NamedListSelector("Delay", &pre_shutter, 100, USE_EEPROM, NULL, 6, - (const char * const[]){"0.1s", "0.5s", "1s", "2s", "4s", "8s"}, - (const int[]){100, 500, 1000, 2000, 4000, 8000}), - new NamedListSelector("Processing Wait", &post_wait, 100, USE_EEPROM, NULL, 7, - (const char * const[]){"0.1s", "0.25s", "0.5s", "1s", "2s", "4s", "8s"}, - (const int[]){100, 250, 500, 1000, 2000, 4000, 8000}), - new NamedListSelector("Shutter Mode", &long_pulse, 0, USE_EEPROM, NULL, 2, - (const char * const[]){"Normal", "Cont Bkt"}, - (const int[]){0, 1}), - new RangeSelector("Shots #", &shots, 1, USE_EEPROM, NULL, 1, 5, 1), - new NamedListSelector("Aspect", &aspect, 23, USE_EEPROM, NULL, 2, - (const char * const[]){"P 2:3", "L 3:2"}, - (const int[]){23, 32}), - new ActionItem("Test Shutter", onTestShutter) - }), + new Menu("Camera", 8, new BaseMenu* const[8] { + new ListSelector("Focal Len", &focal, 35, USE_EEPROM, NULL, 16, + (const int[]){12, 14, 16, 20, 24, 28, 35, 50, 75, 105, 200, 300, 400, 450, 500, 600}), + new NamedListSelector("Shutter", &shutter, 10, USE_EEPROM, NULL, 11, + (const char * const[]){"1/100", "1/50", "1/25", "1/10", "1/4", "0.5s", "1s", "2s", "4s", "8s", "MANUAL"}, + (const int[]){10, 20, 40, 100, 250, 500, 1000, 2000, 4000, 8000, 0}), + new NamedListSelector("Delay", &pre_shutter, 100, USE_EEPROM, NULL, 6, + (const char * const[]){"0.1s", "0.5s", "1s", "2s", "4s", "8s"}, + (const int[]){100, 500, 1000, 2000, 4000, 8000}), + new NamedListSelector("Processing Wait", &post_wait, 100, USE_EEPROM, NULL, 7, + (const char * const[]){"0.1s", "0.25s", "0.5s", "1s", "2s", "4s", "8s"}, + (const int[]){100, 250, 500, 1000, 2000, 4000, 8000}), + new NamedListSelector("Shutter Mode", &long_pulse, 0, USE_EEPROM, NULL, 2, + (const char * const[]){"Normal", "Cont Bkt"}, + (const int[]){0, 1}), + new RangeSelector("Shots #", &shots, 1, USE_EEPROM, NULL, 1, 5, 1), + new NamedListSelector("Aspect", &aspect, 23, USE_EEPROM, NULL, 2, + (const char * const[]){"P 2:3", "L 3:2"}, + (const int[]){23, 32}), + new ActionItem("Test Shutter", onTestShutter) + }), - new NamedListSelector("Motors", &motors_enable, 0, NO_EEPROM, NULL, 2, - (const char * const[]){"On", "Off"}, - (const int[]){1, 0}), + new NamedListSelector("Motors", &motors_enable, 0, NO_EEPROM, NULL, 2, + (const char * const[]){"On", "Off"}, + (const int[]){1, 0}), - new NamedListSelector("Display", &display_invert, 0, NO_EEPROM, NULL, 2, - (const char * const[]){"Dark", "Bright"}, - (const int[]){0, 1}), + new NamedListSelector("Display", &display_invert, 0, NO_EEPROM, NULL, 2, + (const char * const[]){"Dark", "Bright"}, + (const int[]){0, 1}), - new ActionItem("About", onAboutPanoController) -}); + new ActionItem("About", onAboutPanoController) + }); + return menu; +}; diff --git a/pano-controller.ino b/pano-controller.ino index d9164f3..f168d9a 100644 --- a/pano-controller.ino +++ b/pano-controller.ino @@ -7,45 +7,55 @@ * A copy of this license has been included with this distribution in the file LICENSE. */ #include +#include #include +#include +#include +#include "config.h" #include "pano.h" #include "camera.h" #include "hid.h" #include "joystick.h" #include "remote.h" +#include "ble_remote.h" #include "menu.h" #include "display.h" #include "mpu.h" -#if defined(__AVR__) -#error "AVR is not supported" -#elif !defined(ARDUINO_SAMD_FEATHER_M0) -#include "config_teensy.h" -#else -#include "config_feather_m0.h" -#endif - // these variables are modified by the menu volatile int focal, shutter, pre_shutter, post_wait, long_pulse, orientation, aspect, shots, motors_enable, display_invert, horiz, vert, running; static Display display(OLED_RESET); +Adafruit_BluefruitLE_SPI ble(BLUEFRUIT_SPI_CS, BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST); static Camera* camera; static Joystick* joystick; static Remote* remote; +static BLERemote* ble_remote; // HID (Human Interface Device) Combined joystick+remote static AllHID* hid; static MPU* mpu; static DRV8834* horiz_motor; static DRV8834* vert_motor; static Pano* pano; +static Menu* menu; void setup() { Serial.begin(38400); delay(1000); // wait for serial + ble.begin(true); + ble.echo(false); // disable command echo + ble.factoryReset(); + ble.info(); + ble.verbose(false); // turn off debug info + + // LED Activity command is only supported from 0.6.6 + ble.sendCommandCheckOK("AT+HWModeLED=BLEUART"); + ble.sendCommandCheckOK("AT+GAPDEVNAME=Pano Controller"); + display.begin(SSD1306_SWITCHCAPVCC, DISPLAY_I2C_ADDRESS, false); //display.setRotation(2); display.clearDisplay(); @@ -56,18 +66,19 @@ void setup() { camera = new Camera(CAMERA_FOCUS, CAMERA_SHUTTER); joystick = new Joystick(JOYSTICK_SW, JOYSTICK_X, JOYSTICK_Y); remote = new Remote(REMOTE_IN); + ble_remote = new BLERemote(ble); // HID (Human Interface Device) Combined joystick+remote - hid = new AllHID(2, new HID* const[2] {joystick, remote}); + hid = new AllHID(3, new HID* const[3] {joystick, remote, ble_remote}); mpu = new MPU(MPU_I2C_ADDRESS, MPU_INT); mpu->init(); - horiz_motor = new DRV8834(MOTOR_STEPS, HORIZ_DIR, HORIZ_STEP, DRV_M0, DRV_M1); - vert_motor = new DRV8834(MOTOR_STEPS, VERT_DIR, VERT_STEP, DRV_M0, DRV_M1); + horiz_motor = new DRV8834(MOTOR_STEPS, DIR, HORIZ_STEP, nENABLE); + vert_motor = new DRV8834(MOTOR_STEPS, DIR, VERT_STEP); horiz_motor->setMicrostep(32); vert_motor->setMicrostep(32); - pano = new Pano(*horiz_motor, *vert_motor, *camera, *mpu, MOTORS_ON); + pano = new Pano(*horiz_motor, *vert_motor, *camera, *mpu); //pinMode(COMPASS_DRDY, INPUT_PULLUP); @@ -77,6 +88,7 @@ void setup() { analogReadAveraging(32); #endif + menu = getMainMenu(); } int readBattery(void){ @@ -84,9 +96,11 @@ int readBattery(void){ } /* - * Print battery voltage at cursor, format is #.#V (4 chars) + * Add a status overlay (currently battery and bluetooth status) */ -void displayBatteryStatus(void){ +void displayStatusOverlay(void){ + + // Print battery voltage at cursor, format is #.#V (4 chars) int battmV = readBattery(); display.setTextCursor(0, 16); // poor attempt at blinking @@ -95,6 +109,12 @@ void displayBatteryStatus(void){ } display.printf("%2d.%dV", battmV/1000, (battmV % 1000)/100); display.setTextColor(WHITE, BLACK); + + // show a character indicating bluetooth is connected + if (ble_remote->active){ + display.setTextCursor(0,15); + display.print("\xe8"); + } } /* @@ -107,7 +127,7 @@ void displayPanoStatus(void){ display.printf("Photo %d of %d\n", pano->position+1, pano->getHorizShots()*pano->getVertShots()); display.printf("At %d x %d\n", 1+pano->getCurRow(), 1+pano->getCurCol()); displayPanoSize(); - displayBatteryStatus(); + displayStatusOverlay(); displayProgress(); display.display(); } @@ -145,7 +165,7 @@ void displayPanoInfo(void){ displayPanoSize(); display.printf("%d photos\n", pano->getHorizShots()*pano->getVertShots()); displayProgress(); - displayBatteryStatus(); + displayStatusOverlay(); display.display(); } @@ -238,7 +258,7 @@ bool positionCamera(const char *msg, volatile int *horiz, volatile int *vert){ display.setTextCursor(6, 0); displayPanoSize(); display.printf("FOV %d x %d ", pano->horiz_fov, pano->vert_fov); - displayBatteryStatus(); + displayStatusOverlay(); display.display(); } @@ -314,7 +334,7 @@ void executePano(void){ * Update common camera and pano settings from external vars */ void setPanoParams(void){ - menu.cancel(); + menu->cancel(); camera->setAspect(aspect); camera->setFocalLength(focal); pano->setShutter(shutter, pre_shutter, post_wait, long_pulse); @@ -338,7 +358,7 @@ int onStart(int __){ return false; } running = true; - menu.sync(); + menu->sync(); executePano(); return __; } @@ -354,7 +374,7 @@ int onRepeat(int __){ return false; } running = true; - menu.sync(); + menu->sync(); executePano(); return __; } @@ -377,7 +397,7 @@ int on360(int __){ return false; } running = true; - menu.sync(); + menu->sync(); executePano(); return __; } @@ -418,11 +438,11 @@ int onAboutPanoController(int __){ } void onMenuLoop(void){ - displayBatteryStatus(); + displayStatusOverlay(); display.invertDisplay(display_invert); pano->motorsEnable(motors_enable); } void loop() { - displayMenu(menu, display, DISPLAY_ROWS, *hid, onMenuLoop); + displayMenu(*menu, display, DISPLAY_ROWS, *hid, onMenuLoop); } diff --git a/pano.cpp b/pano.cpp index 37d7e79..9d4e7c3 100644 --- a/pano.cpp +++ b/pano.cpp @@ -9,14 +9,12 @@ #include "pano.h" -Pano::Pano(Motor& horiz_motor, Motor& vert_motor, Camera& camera, MPU& mpu, int motors_pin) +Pano::Pano(Motor& horiz_motor, Motor& vert_motor, Camera& camera, MPU& mpu) :horiz_motor(horiz_motor), vert_motor(vert_motor), camera(camera), - mpu(mpu), - motors_pin(motors_pin) + mpu(mpu) { - pinMode(motors_pin, OUTPUT); motorsEnable(false); setFOV(360,180); @@ -232,7 +230,6 @@ void Pano::moveMotorsHome(void){ } void Pano::motorsEnable(bool on){ - digitalWrite(motors_pin, (on) ? HIGH : LOW); + (on) ? horiz_motor.enable() : horiz_motor.disable(); delay(1); - } diff --git a/pano.h b/pano.h index 0d3bae4..abac0e8 100644 --- a/pano.h +++ b/pano.h @@ -38,7 +38,6 @@ class Pano { Motor& vert_motor; Camera& camera; MPU& mpu; - int motors_pin; float horiz_move; float vert_move; int horiz_count; @@ -63,7 +62,7 @@ class Pano { unsigned steady_delay_avg = 100; // configuration - Pano(Motor& horiz_motor, Motor& vert_motor, Camera& camera, MPU& mpu, int motors_pin); + Pano(Motor& horiz_motor, Motor& vert_motor, Camera& camera, MPU& mpu); void setFOV(int horiz_angle, int vert_angle); void setShutter(unsigned shutter_delay, unsigned pre_delay, unsigned post_wait, bool long_pulse); void setShots(unsigned shots); diff --git a/remote.h b/remote.h index 1a2a20d..00d58d3 100644 --- a/remote.h +++ b/remote.h @@ -16,10 +16,6 @@ #include #include -// the remote I have sends repeat code too quick -// allow repeat code only after this many milliseconds -#define REPEAT_DELAY 500 - class Remote : public HID { private: int pin;