Compare commits
No commits in common. "main" and "1a6295bcf76a572b36956bd33e8ba1867da490f7" have entirely different histories.
main
...
1a6295bcf7
29
Readme.md
|
@ -1,12 +1,7 @@
|
|||
# INTVAL 2.0
|
||||
##### Intervalometer for Bolex 16mm Cameras
|
||||
|
||||

|
||||
|
||||
## [Project Home - git.sixteenmillimeter.com/16mm/intval2](https://git.sixteenmillimeter.com/16mm/intval2)
|
||||
|
||||
* Github Mirror - [github.com/sixteenmillimeter/intval2](https://github.com/sixteenmillimeter/intval2)
|
||||
* Gitlab Mirror - [gitlab.com/16mm/intval2](https://gitlab.com/16mm/intval2)
|
||||

|
||||
|
||||
----
|
||||
|
||||
|
@ -80,7 +75,7 @@ laser cutting and .STL files for 3D printing. The .INO file contains the source
|
|||
|
||||
#### A. 3D Printing
|
||||
|
||||

|
||||

|
||||
|
||||
The most time-consuming part of this build *should* be 3D printing the parts for the INTVAL 2.0. Depending on the size of your printing bed it is most likely possible to print all parts in one convenient plate. As will be addressed later, the laser-cuttable elements can also be 3D printed if you lack access to a laser cutter.
|
||||
|
||||
|
@ -90,7 +85,7 @@ The most delicate and integral piece to be fabricated on a 3D printer is the ``m
|
|||
|
||||
#### B. Laser Cutting
|
||||
|
||||

|
||||

|
||||
|
||||
The INTVAL 2.0 has a flat panel on which all parts and electronics are mounted. For this reason, and to reduce production time, a laser cutting component has been added. See the much earlier--and now deprecated--attempt to have an entirely printable model in the original [INTVAL](https://github.com/sixteenmillimeter/INTVAL) (or [INTVAL Next](http://www.thingiverse.com/thing:151944) as I had taken to calling it on [Thingiverse](http://thingiverse.com)). Printing the flat base of this version took upwards of 3 hours on my modest printer, and with the accessibility of laser cutters at hacker/maker spaces becoming more common I decided this was feasible to include in my design.
|
||||
|
||||
|
@ -136,43 +131,43 @@ Additionally you'll need wire and solder of your choosing.
|
|||
|
||||
#### D. Assembly
|
||||
|
||||

|
||||

|
||||
|
||||
Assembling the INTVAL 2.0 can be done in an hour or so, much quicker if practiced. As mentioned, the largest amount of time should be in fabricating the parts or acquiring electronics/parts. There are 2 things I don't like about this design that I'll be addressing in subsequent builds: complexity of the electronics and crowding inside the case.
|
||||
|
||||
##### **Mounting the electronics**
|
||||
|
||||

|
||||

|
||||
|
||||
The L298N is attached to the panel via the three M2 bolts. The space cut in the panel where the fourth would go is to prevent the Bolex's handle from rubbing against the panel. At this point I usually flash the Arduino Trinket Pro with the latest firmware, as in the ["Programming"](#Programming) section, prior to soldering and mounting. It can be done after, though, and can be reprogrammed after it is built.
|
||||
|
||||
##### **Attaching the bearing**
|
||||
|
||||

|
||||

|
||||
|
||||
##### **Mounting the motor's base and microswitch**
|
||||
|
||||

|
||||

|
||||
|
||||
Modify the microswitch as shown in the photos. Be careful not to break the tab by over-exerting it at the bend. At this point, I usually solder leads onto the two tabs as shown below to save a cramped soldering job later. Insert the microswitch into the ``motor_mount_bottom`` and line the piece up with the 6 holes cut in the panel according to the pictures and rendering. It then gets attached to the panel with 4 of the M5 bolts and attached on the bottom with the corresponding M5 nuts.
|
||||
|
||||

|
||||

|
||||
|
||||
##### **Mounting the motor**
|
||||
|
||||

|
||||

|
||||
|
||||
The motor should fit snugly into the ``motor_mount`` piece and can be attached with 2 screws that usually accompany the motor (when bought from Amazon). At this point, I usually solder 2 leads onto the top of the motor and cap it so that the fragile tabs wont break off. I use red and black wires so that I can easily switch their position in the L298N if the intervalometer is functioning backwards.
|
||||
|
||||
##### **Attach buttons**
|
||||
|
||||

|
||||

|
||||
|
||||
Three of the buttons are attached to the corresponding holes in the panel. The size of these holes might have to be modified depending on the type of momentary buttons you settle on. When cutting the panel cover, I will also add an etched-in label for each specifying (left to right) that they control ``direction``, ``speed``, and ``delay``.
|
||||
|
||||
##### **Attaching the DC and 3.5mm sockets**
|
||||
|
||||

|
||||

|
||||
|
||||
Attach the 2.1mm DC socket and the 3.5mm audio socket to the bottom panel of the board, using the bolts to secure them.
|
||||
|
||||
|
@ -191,7 +186,7 @@ Digital pin utilization is as follows:
|
|||
- **PIN 13** - LED indicator (built in to Trinket Pro)
|
||||
- **PIN 19** - Microswitch (aka A5)
|
||||
|
||||
[](./img/intval2_schem.png)
|
||||
[](http://sixteenmillimeter.com/projects/imgs/intval2/intval2_schem.png)
|
||||
|
||||
The Fritzing project requires the following libraries:
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
part,part_id,price,url
|
||||
DC Power Jack,N/A,110,https://amzn.to/4evqnHx
|
||||
12V 120RPM Geared Motor 37mm Gearbox,N/A,1499,https://amzn.to/3YQo1ge
|
|
|
@ -1,51 +0,0 @@
|
|||
# intval2 Quickstart
|
||||
|
||||
## Attaching
|
||||
|
||||
1. Disengage the Bolex motor by switching the lever at position **M** to **O**.
|
||||
2. Set the camera trigger to **M** (opposite **P**).
|
||||
3. Place the intval2 on the camera, lining up the 4 standoff posts with the 4 exposed metal mounting points.
|
||||
4. Line up the intval2 key to the Bolex 1:1 shaft so that it fits snugly.
|
||||
5. Place the 3.5mm screws into 3 of the standoffs, one above the 1:1 shaft, one below and the other in the back top area of the intval2. The standoff underneath the buttons is not accessible.
|
||||
6. Tighten the 3.5mm screws to secure the intval2 to the Bolex.
|
||||
|
||||
## Powering
|
||||
|
||||
Power the device using a 12V DC power source with a 2.1mm center-positive barrel connector.
|
||||
Ensure that the power supply can output at least 1 Amp to ensure there is enough force to drive the motor.
|
||||
You can use a portable battery designed for recharging phones as long as it is capable of outputting 12V.
|
||||
|
||||
Plugging and unplugging the shutter release cable will trigger a frame so ensure that it is plugged in before powering on and removed before powering off if you do not want to unexpectedly advance or rewind the camera.
|
||||
|
||||
## Using
|
||||
|
||||
Using the intval2 is simple and relies on the settings made available by the 3 buttons on the side and the shutter release cable.
|
||||
|
||||
### Shutter Release
|
||||
|
||||
The shutter release cable uses a 3.5mm audio cable and a simple switch to trigger a frame.
|
||||
Pressing the shutter release cable button for longer than 1 second and releasing starts timelapse mode where the camera will shoot frames continuously.
|
||||
The other buttons change settings that will take effect when you trigger each frame.
|
||||
|
||||
Keep in mind that plugging and unplugging the cable while the intval2 is powered on and attached to a Bolex will trigger a frame.
|
||||
|
||||
### Direction
|
||||
|
||||
The direction button, labeled "DIR" and usually red, will change the direction of the camera from forward to reverse by pressing it for longer than 1 second.
|
||||
Press it for less than 1 second to reset it to forward.
|
||||
|
||||
### Exposure
|
||||
|
||||
In normal mode the camera completes a frame in ~500ms, which after accounting for the shutter angle and the prism light loss, is roughly equivalent to 1/5 second exposure.
|
||||
|
||||
Change the exposure of the camera by pressing the button "EXP", usually green, for longer than 1 second.
|
||||
However long you press the button is how long an exposure the camera will make.
|
||||
Once set an exposure will be taken by opening the shutter, waiting for the appropriate amount of time, and then closing the shutter.
|
||||
Press the button for less than 1 second to restore normal exposure.
|
||||
|
||||
### Delay
|
||||
|
||||
The "delay" feature determines how long between frames the intval2 will wait when running in timelapse mode.
|
||||
|
||||
Change the delay between frames by pressing the "DEL" button, usually blue, for longer than 1 second.
|
||||
Similar to the others, press it for less than 1 second to reset the delay to 0.
|
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 71 KiB |
|
@ -1,8 +0,0 @@
|
|||
27c27
|
||||
< const int PIN_MOTOR_FORWARD = 9;
|
||||
---
|
||||
> const int PIN_MOTOR_FORWARD = 12;
|
||||
29c29
|
||||
< const int PIN_MICRO = 19; //laser cut version
|
||||
---
|
||||
> const int PIN_MICRO = 7; //laser cut version
|
|
@ -1,74 +0,0 @@
|
|||
/// mcopy Serial Library
|
||||
|
||||
#include "McopySerial.h"
|
||||
|
||||
McopySerial::McopySerial () {}
|
||||
|
||||
void McopySerial::begin (char identity) {
|
||||
id = identity;
|
||||
Serial.begin(baud);
|
||||
Serial.flush();
|
||||
Serial.setTimeout(serialDelay);
|
||||
}
|
||||
|
||||
char McopySerial::loop () {
|
||||
if (Serial.available()) {
|
||||
cmdChar = (char) Serial.read();
|
||||
_internal();
|
||||
} else {
|
||||
cmdChar = 'z';
|
||||
}
|
||||
return cmdChar;
|
||||
}
|
||||
|
||||
void McopySerial::_internal () {
|
||||
if (cmdChar == DEBUG) {
|
||||
debug(!debugOn);
|
||||
} else if (cmdChar == CONNECT) {
|
||||
_connect();
|
||||
} else if (cmdChar == MCOPY_IDENTIFIER) {
|
||||
_identify();
|
||||
}
|
||||
}
|
||||
|
||||
void McopySerial::_connect () {
|
||||
connected = true;
|
||||
Serial.println(CONNECT);
|
||||
log("connect()");
|
||||
}
|
||||
|
||||
void McopySerial::_identify () {
|
||||
identified = true;
|
||||
Serial.println(id);
|
||||
log("identify()");
|
||||
}
|
||||
|
||||
void McopySerial::debug (bool state) {
|
||||
debugOn = state;
|
||||
log("debug()");
|
||||
}
|
||||
|
||||
void McopySerial::confirm (char cmd) {
|
||||
Serial.println(cmd);
|
||||
}
|
||||
|
||||
void McopySerial::log (String message) {
|
||||
if (debugOn) {
|
||||
Serial.println(message);
|
||||
}
|
||||
}
|
||||
|
||||
String McopySerial::getString () {
|
||||
while (Serial.available() == 0) {
|
||||
//Wait for value string
|
||||
}
|
||||
return Serial.readString();
|
||||
}
|
||||
|
||||
void McopySerial::sendString (String str) {
|
||||
Serial.println(str);
|
||||
}
|
||||
|
||||
void McopySerial::print (String message) {
|
||||
Serial.println(message);
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
#ifndef MCOPY_SERIAL
|
||||
#define MCOPY_SERIAL
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class McopySerial {
|
||||
|
||||
private:
|
||||
|
||||
const uint16_t serialDelay = 5;
|
||||
const uint16_t baud = 57600;
|
||||
|
||||
volatile bool debugOn = false;
|
||||
volatile char cmdChar = 'z';
|
||||
volatile char id;
|
||||
|
||||
void _internal ();
|
||||
void _connect ();
|
||||
void _identify ();
|
||||
|
||||
public:
|
||||
|
||||
volatile bool connected = false;
|
||||
volatile bool identified = false;
|
||||
|
||||
/* CMD FLAGS */
|
||||
const char BLACK = 'b';
|
||||
const char CAMERA = 'c';
|
||||
const char CAMERA_BACKWARD = 'f';
|
||||
const char CAMERA_CAPPER_IDENTIFIER = '8';
|
||||
const char CAMERA_CAPPER_PROJECTOR_IDENTIFIER = '9';
|
||||
const char CAMERA_CAPPER_PROJECTORS_IDENTIFIER = '0';
|
||||
const char CAMERA_CLOSE = 'K';
|
||||
const char CAMERA_EXPOSURE = 'G';
|
||||
const char CAMERA_FORWARD = 'e';
|
||||
const char CAMERA_IDENTIFIER = 'k';
|
||||
const char CAMERA_OPEN = 'J';
|
||||
const char CAMERA_PROJECTORS_IDENTIFIER = '5';
|
||||
const char CAMERA_SECOND = '3';
|
||||
const char CAMERA_SECOND_BACKWARD = '2';
|
||||
const char CAMERA_SECOND_FORWARD = '1';
|
||||
const char CAMERA_SECOND_IDENTIFIER = 'y';
|
||||
const char CAMERA_TIMED = 'n';
|
||||
const char CAMERAS = '4';
|
||||
const char CAMERAS_IDENTIFIER = 'a';
|
||||
const char CAMERAS_PROJECTOR_IDENTIFIER = '6';
|
||||
const char CAMERAS_PROJECTORS_IDENTIFIER = '7';
|
||||
const char CAPPER_IDENTIFIER = 'C';
|
||||
const char CAPPER_OFF = 'B';
|
||||
const char CAPPER_ON = 'A';
|
||||
const char CONNECT = 'i';
|
||||
const char DEBUG = 'd';
|
||||
const char ERROR = 'E';
|
||||
const char HOME = 'I';
|
||||
const char LIGHT = 'l';
|
||||
const char LIGHT_IDENTIFIER = 'o';
|
||||
const char MCOPY_IDENTIFIER = 'm';
|
||||
const char OFFSET = 'O';
|
||||
const char PROJECTOR = 'p';
|
||||
const char PROJECTOR_BACKWARD = 'h';
|
||||
const char PROJECTOR_CAMERA_IDENTIFIER = 's';
|
||||
const char PROJECTOR_CAMERA_LIGHT_IDENTIFIER = 'r';
|
||||
const char PROJECTOR_FORWARD = 'g';
|
||||
const char PROJECTOR_IDENTIFIER = 'j';
|
||||
const char PROJECTOR_LIGHT_IDENTIFIER = 'q';
|
||||
const char PROJECTOR_SECOND = 'w';
|
||||
const char PROJECTOR_SECOND_BACKWARD = 'v';
|
||||
const char PROJECTOR_SECOND_FORWARD = 'u';
|
||||
const char PROJECTOR_SECOND_IDENTIFIER = 't';
|
||||
const char PROJECTORS = 'x';
|
||||
const char PROJECTORS_IDENTIFIER = 'd';
|
||||
const char STATE = 'H';
|
||||
const char TAKEUP_BACKWARD = 'F';
|
||||
const char TAKEUP_FORWARD = 'D';
|
||||
/* END CMD FLAGS */
|
||||
|
||||
McopySerial();
|
||||
|
||||
void begin(char identity);
|
||||
char loop();
|
||||
void confirm(char cmd);
|
||||
String getString();
|
||||
void print(String message);
|
||||
void sendString(String str);
|
||||
|
||||
void debug (bool state);
|
||||
void log (String message);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,428 +0,0 @@
|
|||
#include "McopySerial.h"
|
||||
|
||||
|
||||
//Can be controlled via serial, with mcopy and filmout_manager
|
||||
//Buttons are optional
|
||||
//Exposure controls assumes use of a 120RPM motor
|
||||
//Uses L298N H-bridge breakout board
|
||||
//Target board is an Arduino Nano
|
||||
|
||||
/*
|
||||
----------------------------------------------------
|
||||
Microswitch (use INPUT_PULLUP!!)
|
||||
GND-----\ | \-----PIN
|
||||
----------------------------------------------------
|
||||
*/
|
||||
|
||||
const int MOTOR_RPM = 120;
|
||||
const int BOLEX_C = round((133 / (1.66 * 360)) * 1000); //bolex exposure constant
|
||||
const int FAST_PWM = 255;
|
||||
|
||||
/* ------------------------------------------------
|
||||
* pins
|
||||
* ------------------------------------------------*/
|
||||
//Arduino Nano
|
||||
const int PIN_INDICATOR = 13;
|
||||
const int PIN_MOTOR_FORWARD = 9;
|
||||
const int PIN_MOTOR_BACKWARD = 10;
|
||||
const int PIN_MICRO = 19;
|
||||
//6, 5, 4
|
||||
//1, 2, 3
|
||||
const int BUTTON[4] = {3, 4, 5, 6}; //trigger, delay, speed, direction
|
||||
|
||||
/* ------------------------------------------------
|
||||
* loop
|
||||
* ------------------------------------------------*/
|
||||
const int LOOP_DELAY = 10;
|
||||
|
||||
/* ------------------------------------------------
|
||||
* state
|
||||
* ------------------------------------------------*/
|
||||
volatile int button_state[4] = {1, 1, 1, 1};
|
||||
volatile long button_time[4] = {0, 0, 0, 0};
|
||||
volatile long buttontime = 0;
|
||||
|
||||
volatile boolean sequence = false;
|
||||
volatile boolean running = false;
|
||||
volatile boolean cam_dir = true;
|
||||
volatile boolean delaying = false;
|
||||
volatile boolean timed = false;
|
||||
|
||||
volatile int counter = 0;
|
||||
|
||||
volatile int micro_position = 0;
|
||||
volatile boolean micro_primed = false;
|
||||
|
||||
unsigned long timer = 0;
|
||||
unsigned long frame_start = 0;
|
||||
unsigned long delay_start = 0;
|
||||
|
||||
String timed_str = "600";
|
||||
unsigned long timed_val = 600;
|
||||
unsigned long timed_open = 300; //ms after start_frame to pause
|
||||
volatile boolean timed_paused = false;
|
||||
unsigned long timed_delay = 0;
|
||||
unsigned long timed_last = 0;
|
||||
unsigned long timed_avg = 600;
|
||||
|
||||
volatile int fwd_speed = FAST_PWM;
|
||||
volatile int bwd_speed = FAST_PWM;
|
||||
|
||||
volatile long seq_delay = 42;
|
||||
volatile boolean is_open = false;
|
||||
|
||||
/* ------------------------------------------------
|
||||
* serial
|
||||
* ------------------------------------------------*/
|
||||
McopySerial mc;
|
||||
volatile char cmd_char = 'z';
|
||||
|
||||
const int serialDelay = 5;
|
||||
|
||||
void setup() {
|
||||
mc.begin(mc.CAMERA_IDENTIFIER);
|
||||
PinsInit();
|
||||
ButtonsInit();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
cmd_char = mc.loop();
|
||||
cmd(cmd_char);
|
||||
|
||||
timer = millis();
|
||||
|
||||
Button(0);
|
||||
Button(1);
|
||||
Button(2);
|
||||
Button(3);
|
||||
|
||||
if (sequence && delaying) {
|
||||
WatchDelay();
|
||||
}
|
||||
|
||||
if (running) {
|
||||
if (timed) {
|
||||
ReadTimed();
|
||||
} else {
|
||||
ReadMicro();
|
||||
}
|
||||
}
|
||||
if (!running && !sequence && !delaying){
|
||||
delay(LOOP_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
void cmd (char val) {
|
||||
if (val == mc.CAMERA) {
|
||||
Camera();
|
||||
} else if (val == mc.CAMERA_FORWARD) {
|
||||
CameraDirection(true);
|
||||
} else if (val == mc.CAMERA_BACKWARD) {
|
||||
CameraDirection(false);
|
||||
} else if (val == mc.CAMERA_OPEN) {
|
||||
CameraOpen();
|
||||
} else if (val == mc.CAMERA_CLOSE) {
|
||||
CameraClose();
|
||||
} else if (val == mc.CAMERA_EXPOSURE) {
|
||||
CameraExposure();
|
||||
} else if (val == mc.STATE) {
|
||||
State();
|
||||
}
|
||||
}
|
||||
|
||||
//sending "0" will reset to default exposure time
|
||||
void CameraExposure () {
|
||||
timed_str = mc.getString();
|
||||
timed_val = timed_str.toInt();
|
||||
if (timed_val < 600) {
|
||||
timed_val = 600;
|
||||
timed_str = "600";
|
||||
timed = false;
|
||||
} else {
|
||||
timed_delay = timed_val - BOLEX_C;
|
||||
timed = true;
|
||||
}
|
||||
mc.confirm(mc.CAMERA_EXPOSURE);
|
||||
mc.log("Set exposure time to: ");
|
||||
mc.log(timed_str);
|
||||
}
|
||||
|
||||
void PinsInit () {
|
||||
pinMode(PIN_MOTOR_FORWARD, OUTPUT);
|
||||
pinMode(PIN_MOTOR_BACKWARD, OUTPUT);
|
||||
pinMode(PIN_MICRO, INPUT_PULLUP);
|
||||
pinMode(PIN_INDICATOR, OUTPUT);
|
||||
}
|
||||
|
||||
void ButtonsInit () {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
pinMode(BUTTON[i], INPUT_PULLUP);
|
||||
}
|
||||
}
|
||||
|
||||
void Button (int index) {
|
||||
int val = digitalRead(BUTTON[index]);
|
||||
if (val != button_state[index]) {
|
||||
if (val == LOW) { // pressed
|
||||
button_time[index] = millis();
|
||||
//button_start(index);
|
||||
} else if (val == HIGH) { // not pressed
|
||||
buttontime = millis() - button_time[index];
|
||||
ButtonEnd(index, buttontime);
|
||||
}
|
||||
}
|
||||
button_state[index] = val;
|
||||
}
|
||||
|
||||
/*
|
||||
* dormant for now
|
||||
* void button_start (int index) {
|
||||
if (index == 0) {
|
||||
}
|
||||
}*/
|
||||
|
||||
void ButtonEnd (int index, long buttontime) {
|
||||
if (index == 0) {
|
||||
if (buttontime > 1000) {
|
||||
if (!sequence && !running) {
|
||||
sequence = true;
|
||||
Output(2, 75);
|
||||
Camera();
|
||||
}
|
||||
} else {
|
||||
if (sequence) {
|
||||
sequence = false;
|
||||
//Output(2, 75);
|
||||
} else {
|
||||
Camera();
|
||||
}
|
||||
}
|
||||
} else if (index == 1) { //set delay
|
||||
if (buttontime < 42) {
|
||||
seq_delay = 42;
|
||||
Output(1, 500);
|
||||
} else {
|
||||
seq_delay = buttontime;
|
||||
Output(2, 250);
|
||||
}
|
||||
} else if (index == 2) { // set speed
|
||||
if (buttontime >= 1000) {
|
||||
timed_delay = buttontime - BOLEX_C;
|
||||
timed = true;
|
||||
Output(2, 250);
|
||||
} else if (buttontime < 1000) {
|
||||
timed_delay = 0;
|
||||
timed = false;
|
||||
Output(1, 500);
|
||||
}
|
||||
} else if (index == 3) { //set direction
|
||||
if (buttontime < 1000) {
|
||||
cam_dir = true;
|
||||
Output(1, 500);
|
||||
} else if (buttontime > 1000) {
|
||||
cam_dir = false;
|
||||
Output(2, 250);
|
||||
}
|
||||
}
|
||||
buttontime = 0;
|
||||
}
|
||||
|
||||
void Indicator (boolean state) {
|
||||
if (state) {
|
||||
digitalWrite(PIN_INDICATOR, HIGH);
|
||||
} else {
|
||||
digitalWrite(PIN_INDICATOR, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
void Output (int number, int len) {
|
||||
for (int i = 0; i < number; i++) {
|
||||
Indicator(true);
|
||||
delay(len);
|
||||
Indicator(false);
|
||||
delay(42);
|
||||
}
|
||||
}
|
||||
|
||||
void Camera () {
|
||||
frame_start = millis();
|
||||
if (cam_dir) {
|
||||
analogWrite(PIN_MOTOR_FORWARD, fwd_speed);
|
||||
analogWrite(PIN_MOTOR_BACKWARD, 0);
|
||||
} else {
|
||||
analogWrite(PIN_MOTOR_BACKWARD, bwd_speed);
|
||||
analogWrite(PIN_MOTOR_FORWARD, 0);
|
||||
}
|
||||
running = true;
|
||||
micro_primed = false;
|
||||
}
|
||||
|
||||
void CameraOpen () {
|
||||
if (cam_dir) {
|
||||
analogWrite(PIN_MOTOR_FORWARD, fwd_speed);
|
||||
analogWrite(PIN_MOTOR_BACKWARD, 0);
|
||||
} else {
|
||||
analogWrite(PIN_MOTOR_BACKWARD, bwd_speed);
|
||||
analogWrite(PIN_MOTOR_FORWARD, 0);
|
||||
}
|
||||
running = true;
|
||||
micro_primed = false;
|
||||
|
||||
delay(timed_open);
|
||||
|
||||
analogWrite(PIN_MOTOR_FORWARD, 0);
|
||||
analogWrite(PIN_MOTOR_BACKWARD, 0);
|
||||
|
||||
micro_position = digitalRead(PIN_MICRO);
|
||||
if (micro_position == LOW) {
|
||||
micro_primed = true;
|
||||
}
|
||||
|
||||
mc.confirm(mc.CAMERA_OPEN);
|
||||
mc.log("camera_open()");
|
||||
is_open = true;
|
||||
running = false;
|
||||
}
|
||||
|
||||
void CameraClose () {
|
||||
bool microswitch_open = false;
|
||||
if (is_open) {
|
||||
if (cam_dir) {
|
||||
analogWrite(PIN_MOTOR_FORWARD, fwd_speed);
|
||||
analogWrite(PIN_MOTOR_BACKWARD, 0);
|
||||
} else {
|
||||
analogWrite(PIN_MOTOR_BACKWARD, bwd_speed);
|
||||
analogWrite(PIN_MOTOR_FORWARD, 0);
|
||||
}
|
||||
|
||||
while (!microswitch_open) {
|
||||
micro_position = digitalRead(PIN_MICRO);
|
||||
if (micro_position == HIGH) {
|
||||
microswitch_open = true;
|
||||
}
|
||||
delay(2);
|
||||
}
|
||||
delay(10);
|
||||
|
||||
analogWrite(PIN_MOTOR_FORWARD, 0);
|
||||
analogWrite(PIN_MOTOR_BACKWARD, 0);
|
||||
} else {
|
||||
micro_position = digitalRead(PIN_MICRO);
|
||||
if (micro_position == HIGH) {
|
||||
mc.log("WARNING: Camera already closed");
|
||||
}
|
||||
}
|
||||
mc.confirm(mc.CAMERA_CLOSE);
|
||||
mc.log("camera_close()");
|
||||
is_open = false;
|
||||
}
|
||||
|
||||
boolean ReadDelay () {
|
||||
if (timer - frame_start >= timed_open) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WatchDelay () {
|
||||
if (timer - delay_start >= seq_delay) {
|
||||
delaying = false;
|
||||
Camera();
|
||||
}
|
||||
}
|
||||
|
||||
void ReadTimed () {
|
||||
if (!timed_paused) {
|
||||
if (timer - frame_start > timed_open
|
||||
&& timer - frame_start < timed_open + timed_delay) {
|
||||
PauseTimed();
|
||||
} else if (timer - frame_start > timed_open + timed_delay) {
|
||||
micro_position = digitalRead(PIN_MICRO);
|
||||
if (micro_position == HIGH) {
|
||||
Stop();
|
||||
}
|
||||
delay(2);//smooths out signal
|
||||
}
|
||||
}
|
||||
if (timed_paused && timer - frame_start > timed_open + timed_delay) {
|
||||
StartTimed();
|
||||
}
|
||||
}
|
||||
|
||||
void PauseTimed () {
|
||||
timed_paused = true;
|
||||
analogWrite(PIN_MOTOR_FORWARD, 0);
|
||||
analogWrite(PIN_MOTOR_BACKWARD, 0);
|
||||
}
|
||||
|
||||
void StartTimed () {
|
||||
timed_paused = false;
|
||||
if (cam_dir) {
|
||||
analogWrite(PIN_MOTOR_FORWARD, fwd_speed);
|
||||
analogWrite(PIN_MOTOR_BACKWARD, 0);
|
||||
} else {
|
||||
analogWrite(PIN_MOTOR_BACKWARD, bwd_speed);
|
||||
analogWrite(PIN_MOTOR_FORWARD, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadMicro () {
|
||||
if (ReadDelay()) {
|
||||
micro_position = digitalRead(PIN_MICRO);
|
||||
if (micro_position == LOW
|
||||
&& micro_primed == false) {
|
||||
micro_primed = true;
|
||||
} else if (micro_position == HIGH
|
||||
&& micro_primed == true) {
|
||||
Stop();
|
||||
}
|
||||
delay(2);//smooths out signal
|
||||
}
|
||||
}
|
||||
|
||||
void Stop () {
|
||||
delay(10);
|
||||
analogWrite(PIN_MOTOR_FORWARD, 0);
|
||||
analogWrite(PIN_MOTOR_BACKWARD, 0);
|
||||
|
||||
running = false;
|
||||
micro_primed = false;
|
||||
|
||||
if (cam_dir) {
|
||||
counter += 1;
|
||||
} else {
|
||||
counter -= 1;
|
||||
}
|
||||
|
||||
timed_last = timer - frame_start;
|
||||
timed_avg = (timed_avg + timed_last) / 2;
|
||||
|
||||
mc.confirm(mc.CAMERA);
|
||||
mc.log("Camera completed");
|
||||
mc.log(String(timed_last));
|
||||
if (sequence) {
|
||||
delaying = true;
|
||||
delay_start = millis();
|
||||
}
|
||||
}
|
||||
|
||||
void CameraDirection (boolean state) {
|
||||
cam_dir = state;
|
||||
if (state) {
|
||||
timed_open = 300;
|
||||
mc.confirm(mc.CAMERA_FORWARD);
|
||||
mc.log("camera_direction(true)");
|
||||
} else {
|
||||
timed_open = 400;
|
||||
mc.confirm(mc.CAMERA_FORWARD);
|
||||
mc.log("camera_direction(false)");
|
||||
}
|
||||
}
|
||||
|
||||
void State () {
|
||||
String stateString = String(mc.STATE);
|
||||
stateString += String(mc.CAMERA_EXPOSURE);
|
||||
stateString += String(timed_avg);
|
||||
stateString += String(mc.STATE);
|
||||
mc.sendString(stateString);
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
|
||||
PREFIX=intval2
|
||||
|
||||
OPENSCAD=openscad
|
||||
OPENSCAD_OPTIONS_STL=--enable manifold -D VERBOSE=false
|
||||
OPENSCAD_OPTIONS_DXF=-D VERBOSE=false
|
||||
ORDER_STL=python3 ./c14n_stl.py
|
||||
|
||||
STL=../stl
|
||||
DXF=../dxf
|
||||
MODELS=$(shell cat models.txt | tr '\n' ' ')
|
||||
LASER=$(shell cat laser.txt | tr '\n' ' ')
|
||||
|
||||
all: directories models
|
||||
|
||||
directories:
|
||||
mkdir -p $(STL) $(DXF)
|
||||
|
||||
models: directories $(MODELS) $(LASER)
|
||||
|
||||
clean:
|
||||
rm -f $(STL)/$(PREFIX)_*
|
||||
|
||||
cleanall:
|
||||
rm -rf $(STL)
|
||||
|
||||
# Dependencies for models
|
||||
|
||||
$(MODELS) : $(STL)/$(PREFIX)_%.stl : $(PREFIX).scad
|
||||
$(OPENSCAD) $(OPENSCAD_OPTIONS_STL) -o $@ -D PART=\"$(subst $(PREFIX)_,,$(subst .stl,,$(@F)))\" $<
|
||||
$(ORDER_STL) $@
|
||||
|
||||
$(LASER) : $(DXF)/$(PREFIX)_%.dxf : $(PREFIX).scad
|
||||
$(OPENSCAD) $(OPENSCAD_OPTIONS_DXF) -o $@ -D PART=\"$(subst $(PREFIX)_,,$(subst .dxf,,$(@F)))\" $<
|
117
scad/c14n_stl.py
|
@ -1,117 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# NopSCADlib Copyright Chris Palmer 2018
|
||||
# nop.head@gmail.com
|
||||
# hydraraptor.blogspot.com
|
||||
#
|
||||
# This file is part of NopSCADlib.
|
||||
#
|
||||
# NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License as published by the Free Software Foundation, either version 3 of
|
||||
# the License, or (at your option) any later version.
|
||||
#
|
||||
# NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with NopSCADlib.
|
||||
# If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
#
|
||||
#! OpenSCAD produces randomly ordered STL files. This script re-orders them consistently so that GIT can tell if they have changed or not.
|
||||
#
|
||||
# OpenSCAD produces randomly ordered STL files so source control like GIT can't tell if they have changed or not.
|
||||
# This scrip orders each triangle to start with the lowest vertex first (comparing x, then y, then z)
|
||||
# It then sorts the triangles to start with the one with the lowest vertices first (comparing first vertex, second, then third)
|
||||
# This has no effect on the model but makes the STL consistent. I.e. it makes a canonical form.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
|
||||
def cmz(x):
|
||||
''' Convert "-0" to "0". '''
|
||||
return '0' if x == '-0' else x
|
||||
|
||||
class Vertex:
|
||||
def __init__(self, x, y, z):
|
||||
self.x, self.y, self.z = x, y, z
|
||||
self.key = (float(x), float(y), float(z))
|
||||
|
||||
class Normal:
|
||||
def __init__(self, dx, dy, dz):
|
||||
self.dx, self.dy, self.dz = dx, dy, dz
|
||||
|
||||
class Facet:
|
||||
def __init__(self, normal, v1, v2, v3):
|
||||
self.normal = normal
|
||||
if v1.key < v2.key:
|
||||
if v1.key < v3.key:
|
||||
self.vertices = (v1, v2, v3) #v1 is the smallest
|
||||
else:
|
||||
self.vertices = (v3, v1, v2) #v3 is the smallest
|
||||
else:
|
||||
if v2.key < v3.key:
|
||||
self.vertices = (v2, v3, v1) #v2 is the smallest
|
||||
else:
|
||||
self.vertices = (v3, v1, v2) #v3 is the smallest
|
||||
|
||||
def key(self):
|
||||
return (self.vertices[0].x, self.vertices[0].y, self.vertices[0].z,
|
||||
self.vertices[1].x, self.vertices[1].y, self.vertices[1].z,
|
||||
self.vertices[2].x, self.vertices[2].y, self.vertices[2].z)
|
||||
|
||||
class STL:
|
||||
def __init__(self, fname):
|
||||
self.facets = []
|
||||
|
||||
with open(fname) as f:
|
||||
words = [cmz(s.strip()) for s in f.read().split()]
|
||||
|
||||
if words[0] == 'solid' and words[1] == 'OpenSCAD_Model':
|
||||
i = 2
|
||||
while words[i] == 'facet':
|
||||
norm = Normal(words[i + 2], words[i + 3], words[i + 4])
|
||||
v1 = Vertex(words[i + 8], words[i + 9], words[i + 10])
|
||||
v2 = Vertex(words[i + 12], words[i + 13], words[i + 14])
|
||||
v3 = Vertex(words[i + 16], words[i + 17], words[i + 18])
|
||||
i += 21
|
||||
self.facets.append(Facet(norm, v1, v2, v3))
|
||||
|
||||
self.facets.sort(key = Facet.key)
|
||||
else:
|
||||
print("Not an OpenSCAD ascii STL file")
|
||||
sys.exit(1)
|
||||
|
||||
def write(self, fname):
|
||||
mins = [float('inf'), float('inf'), float('inf')]
|
||||
maxs = [float('-inf'), float('-inf'), float('-inf')]
|
||||
with open(fname,"wt") as f:
|
||||
print('solid OpenSCAD_Model', file=f)
|
||||
for facet in self.facets:
|
||||
print(' facet normal %s %s %s' % (facet.normal.dx, facet.normal.dy, facet.normal.dz), file=f)
|
||||
print(' outer loop', file=f)
|
||||
for vertex in facet.vertices:
|
||||
print(' vertex %s %s %s' % (vertex.x, vertex.y, vertex.z), file=f)
|
||||
for i in range(3):
|
||||
ordinate = vertex.key[i]
|
||||
if ordinate > maxs[i]: maxs[i] = ordinate
|
||||
if ordinate < mins[i]: mins[i] = ordinate
|
||||
print(' endloop', file=f)
|
||||
print(' endfacet', file=f)
|
||||
print('endsolid OpenSCAD_Model', file=f)
|
||||
return mins, maxs
|
||||
|
||||
def canonicalise(fname):
|
||||
stl = STL(fname)
|
||||
return stl.write(fname)
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) == 2:
|
||||
canonicalise(sys.argv[1])
|
||||
else:
|
||||
print("\nusage:\n\t c14n_stl file - Canonicalise an STL file created by OpenSCAD.")
|
||||
sys.exit(1)
|
1072
scad/intval2.scad
|
@ -1,3 +0,0 @@
|
|||
../dxf/intval2_laser_plate.dxf
|
||||
../dxf/intval2_panel.dxf
|
||||
../dxf/intval2_panel_cover.dxf
|
|
@ -1,27 +0,0 @@
|
|||
../stl/intval2_motor_key.stl
|
||||
../stl/intval2_motor_key_reinforced.stl
|
||||
../stl/intval2_motor_key_reinforced_roller.stl
|
||||
../stl/intval2_motor_key_120.stl
|
||||
../stl/intval2_motor_key_120_reinforced.stl
|
||||
../stl/intval2_motor_key_120_reinforced_roller.stl
|
||||
../stl/intval2_motor_mount_bottom.stl
|
||||
../stl/intval2_motor_mount_top.stl
|
||||
../stl/intval2_motor_mount_top_120.stl
|
||||
../stl/intval2_motor_mount_top_reinforced.stl
|
||||
../stl/intval2_electronics_mount.stl
|
||||
../stl/intval2_motor_cap.stl
|
||||
../stl/intval2_motor_cap_120.stl
|
||||
../stl/intval2_bolt_guide.stl
|
||||
../stl/intval2_standoff.stl
|
||||
../stl/intval2_bearing_reinforcement.stl
|
||||
../stl/intval2_key_cap.stl
|
||||
../stl/intval2_case_standoff.stl
|
||||
../stl/intval2_trinket_mount.stl
|
||||
../stl/intval2_l298N_mount.stl
|
||||
../stl/intval2_plate.stl
|
||||
../stl/intval2_plunger_plate.stl
|
||||
../stl/intval2_standoff_plate.stl
|
||||
../stl/intval2_button_nuts_plate.stl
|
||||
../stl/intval2_printed_panel.stl
|
||||
../stl/intval2_printed_panel_cover_buttons.stl
|
||||
../stl/intval2_printed_panel_cover.stl
|
|
@ -1,19 +1,6 @@
|
|||
//!OpenSCAD
|
||||
/* preprocessor */
|
||||
|
||||
IN = 25.4;
|
||||
MM = 1;
|
||||
|
||||
function R (diameter) = diameter / 2.0;
|
||||
|
||||
function IN2MM(in) = in * IN;
|
||||
|
||||
function MM2IN(mm) = mm / IN;
|
||||
|
||||
module hex (diag = 10, h = 1) {
|
||||
cylinder(r = diag / 2, h = h, center = true, $fn = 6);
|
||||
}
|
||||
|
||||
module tube(o = 1, i = 0, h = 1, center = false, $fn = 12) {
|
||||
$fn = $fn;
|
||||
union () {
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
SCRIPT_NAME="intval2"
|
||||
SCAD="scad/${SCRIPT_NAME}.scad"
|
||||
|
||||
listParts () {
|
||||
cat "${1}" | grep 'PART ==' | grep -v 'debug' | awk -F'"' '{print $2}'
|
||||
}
|
||||
|
||||
listLaser () {
|
||||
cat "${1}" | grep 'LASER ==' | grep -v 'debug' | awk -F'"' '{print $2}'
|
||||
}
|
||||
|
||||
renderPart () {
|
||||
part="${1}"
|
||||
echo "renderPart ${part}"
|
||||
stl="stl/${SCRIPT_NAME}_${part}.stl"
|
||||
openscad --export-format asciistl --enable manifold -o "${stl}" -D "LASER=\"\";" -D "PART=\"${part}\";" "${SCAD}"
|
||||
python3 scad/c14n_stl.py "${stl}"
|
||||
}
|
||||
|
||||
renderLaser() {
|
||||
laser="${1}"
|
||||
echo "renderLaser ${laser}"
|
||||
dxf="dxf/${SCRIPT_NAME}_${laser}.dxf"
|
||||
openscad -o "${dxf}" -D "PART=\"\";" -D "LASER=\"${laser}\";" "${SCAD}"
|
||||
}
|
||||
|
||||
allParts () {
|
||||
PARTS=($(listParts "${SCAD}"))
|
||||
for part in "${PARTS[@]}"; do
|
||||
renderPart "${part}"
|
||||
done
|
||||
|
||||
LASERS=($(listLaser "${SCAD}"))
|
||||
for laser in "${LASERS[@]}"; do
|
||||
renderLaser "${laser}"
|
||||
done
|
||||
}
|
||||
|
||||
allParts
|