Compare commits

..

45 Commits

Author SHA1 Message Date
Matt McWilliams 3a8dce573f Render and normalize all parts. Use manifold option. 2024-11-28 20:24:48 -05:00
Matt McWilliams ad6d61fdd2 First draft of intval2.3, a version geared towards mcopy compatibility and use with filmout_manager 2024-11-28 20:21:41 -05:00
Matt McWilliams 28a7655c4a Update the printed panel and the printed panel cover. Started building a BOM. 2024-11-13 21:54:55 -05:00
Matt McWilliams d32338e067 Fixed rendering of printed panel. Removed panel_printed.stl (dumb). 2024-11-12 11:02:44 -05:00
Matt McWilliams cdd74ede56 Add printed covers and update panel_printed with new bolts added 2024-11-12 00:37:05 -05:00
Matt McWilliams 5727d171a6 Cleanup to OpenSCAD code. Add sorting to makefile. Use new linter for Openscad to clean up a lot of magic numbers. 2024-11-11 22:43:45 -05:00
Matt McWilliams f061e0d180 Rename the docs readme from quickstart 2024-11-11 22:42:27 -05:00
Matt McWilliams 000844e263 Add a set screw. Create a quickstart guide. 2024-10-08 09:30:50 -04:00
Matt McWilliams b4807e1875 img not imgs 2024-06-04 20:55:20 -04:00
Matt McWilliams f7bd6bd95d fixed bad image reference 2024-06-04 20:54:27 -04:00
Matt McWilliams 664e189f37 Move images into repo. Make local references 2024-06-04 20:52:12 -04:00
Matt McWilliams 12f88fbb95 Fix laser render. Re-render all DXF files 2024-05-28 22:22:21 -04:00
Matt McWilliams f1c2ec94d9 Accidentally rendered all files wrong. This corrects that. Add laser rendering of DXF files. 2024-05-28 21:42:58 -04:00
Matt McWilliams cb950f3f85 Correct wrongly-named l298N throughout SCAD file 2024-05-28 21:17:46 -04:00
Matt McWilliams 3f409f4805 Delete wrongly-named stl 2024-05-28 21:16:26 -04:00
Matt McWilliams b3e6412598 Render all previously un-rendered STLs 2024-05-28 21:13:34 -04:00
Matt McWilliams 4feec7501e Add script for rendering all STLs 2024-05-28 21:13:13 -04:00
Matt McWilliams df74b30113 Tweaks needed for printing design 2023-12-22 19:10:29 -05:00
Matt McWilliams 6da3406254 Intval logo medallion 2023-09-12 20:40:18 -04:00
Matt McWilliams 1de4de6e1c Create an alternate motor cap for bigger motors 2023-06-29 17:20:23 -04:00
Matt McWilliams 298f38ced9 cleanup 2023-05-09 18:51:21 -04:00
Matt McWilliams 013e15cdea Create an arduino_nano electronics mount 2023-05-02 23:57:57 -04:00
Matt McWilliams 873093fa4c Printable panel cover. Delete original intval1 design. 2023-04-28 19:20:54 -04:00
Matt McWilliams c99a6166c0 Render the printed panel. 2023-04-17 14:48:55 -04:00
Matt McWilliams ced940a858 Make a printable version of the intval2 laser cut panel. 2023-04-17 14:48:26 -04:00
Matt McWilliams 71e175a0a2 Add new project home and mirrors 2023-04-07 09:28:31 -04:00
Matt McWilliams a783907da6 Use the sort-stl feature if it is available (not on mac?) 2023-04-05 23:02:45 -04:00
Matt McWilliams 0033f9669c Update re-inforced roller 2023-04-05 18:12:02 -04:00
Matt McWilliams 53ef52ff89 Fix the 120 roller 2022-12-28 10:27:49 -05:00
Matt McWilliams 14677e9128 Re-render everying after error 2022-12-21 17:14:55 -05:00
Matt McWilliams 349d5694dc Render missing stls 2022-12-20 15:56:46 -05:00
Matt McWilliams 51cd4af519 Add a reinforced motor mount top for alternate design 2022-12-20 15:20:25 -05:00
Matt McWilliams 8718e46263 Add additional models to build list 2022-12-20 13:22:28 -05:00
Matt McWilliams e687c5386e Merge remote-tracking branch 'origin/main' into main 2022-12-20 13:19:35 -05:00
Matt McWilliams dd44a7cdad Add a patch for an alternate build 2022-12-20 13:19:28 -05:00
Matt McWilliams b3c97597b0 Updated washer standoff 2022-10-08 15:37:23 -04:00
Matthew McWilliams b8d9a8be2f Built all models 2022-10-08 10:23:33 -04:00
Matt McWilliams 9df82ffd4c Add remaining parts to Makefile 2022-10-08 10:11:12 -04:00
Matthew McWilliams e45062c153 Create Makefile and compile individual STL and DXF files for printing and laser cutting 2022-10-07 08:38:34 -04:00
Matt McWilliams 1f13ba5c0f Save standoff washer design for repairs 2022-09-24 14:54:55 -04:00
Matt McWilliams c2125cad65 Add void for standoff on bearing reinforcement part 2022-09-20 21:06:11 -04:00
Matthew McWilliams 48c9824b84 Merge remote-tracking branch 'origin/main' into main 2022-09-17 20:50:54 -04:00
Matt McWilliams 244239dab5 Slight adjustmetn forPrusa 2022-09-17 15:34:01 -04:00
Matthew McWilliams b94d7fc106 Flatten side of bearing reinforcement to prevent obstructing the bolex variable shutter 2022-09-16 14:21:28 -04:00
Matthew McWilliams b92afebcd2 Add a bearing reinforcement piece 2022-09-16 11:05:55 -04:00
61 changed files with 901591 additions and 424 deletions

View File

@ -1,7 +1,12 @@
# INTVAL 2.0
##### Intervalometer for Bolex 16mm Cameras
![Photo of finished INTVAL](http://sixteenmillimeter.com/projects/imgs/intval2/intval2_complete.jpg)
![Photo of finished INTVAL](./img/intval2_complete.jpg)
## [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)
----
@ -75,7 +80,7 @@ laser cutting and .STL files for 3D printing. The .INO file contains the source
#### A. 3D Printing
![Plate of all 3D printed components](http://sixteenmillimeter.com/projects/imgs/intval2/plate_render.png)
![Plate of all 3D printed components](./img/plate_render.png)
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.
@ -85,7 +90,7 @@ The most delicate and integral piece to be fabricated on a 3D printer is the ``m
#### B. Laser Cutting
![Plate of all laser cut components](http://sixteenmillimeter.com/projects/imgs/intval2/dxf_plate_render.png)
![Plate of all laser cut components](./img/dxf_plate_render.png)
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.
@ -131,43 +136,43 @@ Additionally you'll need wire and solder of your choosing.
#### D. Assembly
![Photo of exploded view](http://sixteenmillimeter.com/projects/imgs/intval2/exploded_view.png)
![Photo of exploded view](./img/exploded_view.png)
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**
![Photo of L298N mounted to panel](http://sixteenmillimeter.com/projects/imgs/intval2/L298N_mounted.jpg)
![Photo of L298N mounted to panel](./img/L298N_mounted.jpg)
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**
![Photo of panel with bearing attached](http://sixteenmillimeter.com/projects/imgs/intval2/bearing_mounted.jpg)
![Photo of panel with bearing attached](./img/bearing_mounted.jpg)
##### **Mounting the motor's base and microswitch**
![Photo of Microswitch and modification made to it](http://sixteenmillimeter.com/projects/imgs/intval2/microswitch_modified.jpg)
![Photo of Microswitch and modification made to it](./img/microswitch_modified.jpg)
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.
![Photo of panel with motor mount bottom attached](http://sixteenmillimeter.com/projects/imgs/intval2/motor_bottom_mounted.jpg)
![Photo of panel with motor mount bottom attached](./img/motor_bottom_mounted.jpg)
##### **Mounting the motor**
![Photo of motor ready to mount on panel](http://sixteenmillimeter.com/projects/imgs/intval2/motor_mounted.jpg)
![Photo of motor ready to mount on panel](./img/motor_mounted.jpg)
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**
![Photo of buttons attached to panel cover](http://sixteenmillimeter.com/projects/imgs/intval2/buttons_attached.jpg)
![Photo of buttons attached to panel cover](./img/buttons_attached.jpg)
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**
![Photo of DC and 3.5mm sockets on](http://sixteenmillimeter.com/projects/imgs/intval2/dc_audio_mounted.jpg)
![Photo of DC and 3.5mm sockets on](./img/dc_audio_mounted.jpg)
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.
@ -186,7 +191,7 @@ Digital pin utilization is as follows:
- **PIN 13** - LED indicator (built in to Trinket Pro)
- **PIN 19** - Microswitch (aka A5)
[![Fritzing schematic](http://sixteenmillimeter.com/projects/imgs/intval2/intval2_schem_512.png)](http://sixteenmillimeter.com/projects/imgs/intval2/intval2_schem.png)
[![Fritzing schematic](./img/intval2_schem_512.png)](./img/intval2_schem.png)
The Fritzing project requires the following libraries:

3
bom/prices.csv Normal file
View File

@ -0,0 +1,3 @@
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 part part_id price url
2 DC Power Jack N/A 110 https://amzn.to/4evqnHx
3 12V 120RPM Geared Motor 37mm Gearbox N/A 1499 https://amzn.to/3YQo1ge

51
docs/Readme.md Normal file
View File

@ -0,0 +1,51 @@
# 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.

6156
dxf/intval2_laser_plate.dxf Normal file

File diff suppressed because it is too large Load Diff

3292
dxf/intval2_panel.dxf Normal file

File diff suppressed because it is too large Load Diff

3000
dxf/intval2_panel_cover.dxf Normal file

File diff suppressed because it is too large Load Diff

BIN
img/L298N_mounted.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
img/bearing_mounted.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
img/buttons_attached.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
img/dc_audio_mounted.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
img/dxf_plate_render.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
img/exploded_view.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
img/intval2_complete.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
img/intval2_schem.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
img/intval2_schem_512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
img/motor_mounted.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
img/plate_render.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View File

@ -0,0 +1,8 @@
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

View File

@ -0,0 +1,74 @@
/// 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);
}

View File

@ -0,0 +1,91 @@
#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

View File

@ -0,0 +1,428 @@
#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);
}

34
scad/Makefile Normal file
View File

@ -0,0 +1,34 @@
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 Normal file
View File

@ -0,0 +1,117 @@
#!/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)

File diff suppressed because it is too large Load Diff

3
scad/laser.txt Normal file
View File

@ -0,0 +1,3 @@
../dxf/intval2_laser_plate.dxf
../dxf/intval2_panel.dxf
../dxf/intval2_panel_cover.dxf

27
scad/models.txt Normal file
View File

@ -0,0 +1,27 @@
../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

View File

@ -1,6 +1,19 @@
//!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 () {

39
scripts/scad.sh Normal file
View File

@ -0,0 +1,39 @@
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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

7240
stl/intval2_bolt_guide.stl Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2214
stl/intval2_key_cap.stl Normal file

File diff suppressed because it is too large Load Diff

25972
stl/intval2_l298N_mount.stl Normal file

File diff suppressed because it is too large Load Diff

16312
stl/intval2_logo.stl Normal file

File diff suppressed because it is too large Load Diff

5042
stl/intval2_motor_cap.stl Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

18748
stl/intval2_motor_key.stl Normal file

File diff suppressed because it is too large Load Diff

18370
stl/intval2_motor_key_120.stl Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

208070
stl/intval2_plate.stl Normal file

File diff suppressed because it is too large Load Diff

16564
stl/intval2_plunger_plate.stl Normal file

File diff suppressed because it is too large Load Diff

120374
stl/intval2_printed_panel.stl Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

18482
stl/intval2_standoff.stl Normal file

File diff suppressed because it is too large Load Diff

73922
stl/intval2_standoff_plate.stl Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff