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
-
-
### 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;