Restructure project with OpenSCAD redesign and v1 legacy code
- Move legacy FreeCAD files to v1/ - Add OpenSCAD programmatic CAD designs - Add README and AGENTS.md documentation - Add .gitignore - Update firmware to v2 architecture
This commit is contained in:
parent
468cac4dff
commit
0fbf33b756
28
.gitignore
vendored
Normal file
28
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Arduino / ESP32
|
||||
*.ino.cpp
|
||||
*.lst
|
||||
*.elf
|
||||
*.eep
|
||||
*.hex
|
||||
|
||||
# Build directories
|
||||
build/
|
||||
|
||||
# PlatformIO
|
||||
.pio/
|
||||
.vscode/.pf-*
|
||||
|
||||
# VS Code
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Backup files
|
||||
*~
|
||||
*.bak
|
||||
*.swp
|
||||
213
AGENTS.md
Normal file
213
AGENTS.md
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
# AGENTS.md - Sirens Project
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is an Arduino/ESP32 embedded systems project for controlling sirens. The codebase consists of multiple Arduino sketches (.ino files) for different hardware components.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
sirens/
|
||||
├── src/
|
||||
│ ├── siren_remote_control_esp32_v2/ # Remote control unit
|
||||
│ │ └── siren_remote_control_esp32_v2.ino
|
||||
│ ├── siren_controller_esp32_simple_station_v2_multi/ # Station controller
|
||||
│ │ └── siren_controller_esp32_simple_station_v2_multi.ino
|
||||
│ └── B_G431B_ESC1_motor_controller/ # SimpleFOC motor controller
|
||||
│ └── B_G431B_ESC1_motor_controller.ino
|
||||
├── v1/ # Version 1 designs
|
||||
├── designs/ # CAD files (OpenSCAD, STL)
|
||||
└── AGENTS.md # This file
|
||||
```
|
||||
|
||||
## Build Commands
|
||||
|
||||
### Current State
|
||||
- **No command-line build system configured** - Uses Arduino IDE
|
||||
- Upload via Arduino IDE or PlatformIO (recommended for future)
|
||||
|
||||
### Future: PlatformIO Migration (TODO)
|
||||
```bash
|
||||
# Once PlatformIO is configured:
|
||||
pio run # Build all projects
|
||||
pio run -e <environment> # Build specific environment
|
||||
pio run -t upload # Upload to device
|
||||
pio device monitor # Monitor serial output
|
||||
```
|
||||
|
||||
### Running a Single Test
|
||||
- **No unit tests exist** - Embedded projects typically test via hardware
|
||||
- For now, manual testing on physical hardware is required
|
||||
|
||||
## Libraries
|
||||
|
||||
The project uses the following Arduino/ESP32 libraries:
|
||||
|
||||
| Library | Purpose |
|
||||
|---------|---------|
|
||||
| WiFi | ESP32 WiFi connectivity |
|
||||
| Wire | I2C communication |
|
||||
| SSD1306Ascii / SSD1306AsciiWire | OLED display (128x64) |
|
||||
| ArduinoOSCWiFi | OSC protocol over WiFi |
|
||||
| AiEsp32RotaryEncoder | Rotary encoder input |
|
||||
| arduino-timer | Timer/async operations |
|
||||
| Preferences | ESP32 NVS storage |
|
||||
| SimpleFOC | Motor control (B-G431B-ESC1) |
|
||||
| SimpleFOCDrivers | SimpleFOC driver utilities |
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
### General Conventions
|
||||
|
||||
- **Language**: C++ (Arduino framework)
|
||||
- **File extension**: `.ino` (Arduino sketch)
|
||||
- **Naming**: camelCase for variables and functions
|
||||
- **Constants**: UPPER_SNAKE_CASE with `const` or `#define`
|
||||
|
||||
### Arduino Sketch Structure
|
||||
|
||||
```cpp
|
||||
// 1. Includes
|
||||
#include "WiFi.h"
|
||||
#include <Wire.h>
|
||||
#include <SSD1306Ascii.h>
|
||||
|
||||
// 2. Global constants
|
||||
const int sirenCount = 4;
|
||||
const char* ssid = "sirening";
|
||||
|
||||
// 3. Global variables
|
||||
int menuSelect = 0;
|
||||
|
||||
// 4. Function prototypes (if needed)
|
||||
void setupEncoderButton(AiEsp32RotaryEncoder& eb, char* val);
|
||||
|
||||
// 5. setup() - runs once at boot
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
// initialization code...
|
||||
}
|
||||
|
||||
// 6. loop() - runs continuously
|
||||
void loop() {
|
||||
// main logic...
|
||||
}
|
||||
```
|
||||
|
||||
### Functions
|
||||
|
||||
- Keep functions focused and reasonably sized (<100 lines)
|
||||
- Use descriptive names: `updateFreq()`, `killAll()`, `setupOSC()`
|
||||
- Group related functions together
|
||||
- Use helper functions to reduce duplication in loop()
|
||||
|
||||
### Types
|
||||
|
||||
- Use explicit types: `int`, `float`, `bool`, `unsigned long`
|
||||
- Prefer `const` for values that won't change
|
||||
- Use `float` for floating-point values (no `double` on ESP32)
|
||||
|
||||
### Error Handling
|
||||
|
||||
- Return early on errors:
|
||||
```cpp
|
||||
if(!currentSense.init()){
|
||||
Serial.println("Current sense init failed.");
|
||||
return;
|
||||
}
|
||||
```
|
||||
- Use Serial for debugging output with descriptive messages
|
||||
- Validate input ranges before use:
|
||||
```cpp
|
||||
if (input < 500.0 && input >= 0.0) {
|
||||
updateFreq(input);
|
||||
}
|
||||
```
|
||||
|
||||
### Interrupt Service Routines (ISR)
|
||||
|
||||
- Mark ISRs with `IRAM_ATTR` on ESP32:
|
||||
```cpp
|
||||
void IRAM_ATTR readFreqEncoderISR() {
|
||||
encoderButton1.readEncoder_ISR();
|
||||
}
|
||||
```
|
||||
- Keep ISRs minimal - only do essential work
|
||||
|
||||
### Display/UI Code
|
||||
|
||||
- Use helper functions for display updates:
|
||||
```cpp
|
||||
void updateDisplayValue(String string) {
|
||||
oled.set2X();
|
||||
oled.setCursor(12, 5);
|
||||
oled.print(string);
|
||||
oled.clearToEOL();
|
||||
}
|
||||
```
|
||||
|
||||
### OSC Communication
|
||||
|
||||
- Use lambda callbacks for OSC subscriptions:
|
||||
```cpp
|
||||
OscWiFi.subscribe(settings_port, "/freq", [](const OscMessage& m) {
|
||||
float input = m.arg<float>(0);
|
||||
// handle message...
|
||||
});
|
||||
```
|
||||
|
||||
### Preferences (NVS)
|
||||
|
||||
- Open with explicit mode:
|
||||
```cpp
|
||||
prefs.begin("prefs", RO_MODE); // false = read/write, true = read only
|
||||
prefs.putUInt("ports", ports);
|
||||
prefs.getUInt("ports");
|
||||
prefs.end();
|
||||
```
|
||||
|
||||
### Serial Communication
|
||||
|
||||
- Use `Serial.begin(115200)` for debug output
|
||||
- Use `HardwareSerial` for additional UARTs:
|
||||
```cpp
|
||||
HardwareSerial freqSerial(1);
|
||||
freqSerial.begin(115200, SERIAL_8N1, 18, 19);
|
||||
```
|
||||
|
||||
### Comments
|
||||
|
||||
- Use TODO comments for future work:
|
||||
```cpp
|
||||
//TODO: These should be bound to a publication of the frequency directly from the siren
|
||||
```
|
||||
- Comment out disabled code with explanation
|
||||
- Keep comments brief and purposeful
|
||||
|
||||
### Formatting
|
||||
|
||||
- Use 2-space indentation (Arduino default)
|
||||
- Opening brace on same line
|
||||
- Space after keywords: `if (` not `if(`
|
||||
|
||||
## Future Improvements
|
||||
|
||||
1. **Add PlatformIO**: Create `platformio.ini` for command-line builds
|
||||
2. **Add unit tests**: Use PlatformIO unit testing for non-hardware logic
|
||||
3. **Consolidate common code**: Extract shared utilities into a common library
|
||||
4. **Add CI/CD**: GitHub Actions for build verification
|
||||
5. **Document hardware**: Add wiring diagrams and pinouts
|
||||
|
||||
## Hardware Targets
|
||||
|
||||
- **ESP32** (WiFi + BLE)
|
||||
- **B-G431B-ESC1** motor controller (SimpleFOC compatible)
|
||||
- **OLED Display**: SSD1306 128x64 I2C
|
||||
- **Rotary Encoder**: AiEsp32RotaryEncoder
|
||||
|
||||
## Development Notes
|
||||
|
||||
- Serial monitor uses 115200 baud
|
||||
- WiFi AP: `sirening` / `alarm_11735`
|
||||
- OSC ports: 54000 (settings), 54001 (state)
|
||||
- Motor control uses FOC (Field-Oriented Control) via SimpleFOC
|
||||
163
README.md
163
README.md
|
|
@ -0,0 +1,163 @@
|
|||
# Tunable Sirens
|
||||
|
||||
A programmable, tunable siren system using BLDC motors with FOC (Field-Oriented Control).
|
||||
|
||||
## Overview
|
||||
|
||||
This project implements a tunable siren where the frequency is controlled remotely via OSC (Open Sound Control) messages over WiFi. The system uses a DJI 2212 920KV BLDC motor driven by a B-G431B-ESC1 controller running SimpleFOC. Multiple stations can be controlled independently or simultaneously.
|
||||
|
||||
## Hardware
|
||||
|
||||
| Component | Part |
|
||||
|-----------|------|
|
||||
| Motor | DJI 2212 920KV BLDC |
|
||||
| Motor Controller | STMicroelectronics B-G431B-ESC1 |
|
||||
| Processors | ESP32 WROOM (one per station, one for remote) |
|
||||
| Display | SSD1306 128x64 OLED (on both remote and station) |
|
||||
| Input | AiEsp32 Rotary Encoder (on both remote and station) |
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌──────────────────────┐
|
||||
│ Remote Control │
|
||||
│ (ESP32 + OLED) │
|
||||
┌─────────────────┐ OSC (WiFi) │ 192.168.4.200 │
|
||||
│ External OSC │ ───────────────┤ Port: 54001 │
|
||||
│ (Any client) │ │ - Acts as WiFi AP │
|
||||
└─────────────────┘ │ - Routes to stations│
|
||||
└──────────┬───────────┘
|
||||
│ OSC forward
|
||||
│ (optional path)
|
||||
▼
|
||||
┌────────────────┐ ┌────────────────┐
|
||||
│ Station 1 │ │ Station N │
|
||||
│ 192.168.4.201 │ ... │ 192.168.4.20X │
|
||||
│ Port: 54000 │ │ Port: 54000 │
|
||||
└───────┬────────┘ └────────┬────────┘
|
||||
│ │
|
||||
│ ┌─────────────────┴─────────┐
|
||||
└────────►│ B-G431B-ESC1 │
|
||||
│ SimpleFOC │
|
||||
│ DJI 2212 920KV BLDC │
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
## Communication Modes
|
||||
|
||||
### 1. Via Remote Control (Router Mode)
|
||||
|
||||
The remote control acts as a WiFi access point and forwards OSC messages to stations. This allows controlling multiple sirens from a single entry point.
|
||||
|
||||
- **Remote IP**: `192.168.4.200`
|
||||
- **Port**: `54001` (receives commands)
|
||||
|
||||
### 2. Direct to Station
|
||||
|
||||
Send OSC messages directly to each station's IP address:
|
||||
|
||||
- **Station 1**: `192.168.4.201:54000`
|
||||
- **Station 2**: `192.168.4.202:54000`
|
||||
- And so on...
|
||||
|
||||
### WiFi Setup
|
||||
|
||||
- **SSID**: `sirening`
|
||||
- **Password**: `alarm_11735`
|
||||
- **IP Range**: `192.168.4.200` (remote) + `201-208` (stations)
|
||||
|
||||
## OSC Protocol
|
||||
|
||||
### Messages
|
||||
|
||||
| Address | Arguments | Description |
|
||||
|---------|-----------|-------------|
|
||||
| `/freq` | `motorIndex, frequency` | Set frequency in Hz (0-500) |
|
||||
| `/kill` | — | Stop siren (set frequency to 0) |
|
||||
| `/state` | `motorIndex, frequency` | Station reports current state |
|
||||
|
||||
### Example (Python)
|
||||
|
||||
```python
|
||||
from pythonosc import osc_message_builder
|
||||
from pythonosc.udp_client import SimpleUDPClient
|
||||
|
||||
client = SimpleUDPClient("192.168.4.201", 54000)
|
||||
|
||||
# Set frequency to 100Hz for motor 1
|
||||
msg = osc_message_builder.OscMessageBuilder(address="/freq")
|
||||
msg.add_arg(1) # motorIndex (1-indexed)
|
||||
msg.add_arg(100.0) # frequency in Hz
|
||||
client.send(msg.build())
|
||||
|
||||
# Kill/stop the siren
|
||||
msg = osc_message_builder.OscMessageBuilder(address="/kill")
|
||||
client.send(msg.build())
|
||||
```
|
||||
|
||||
### Station Response
|
||||
|
||||
The station reports its state back on port `54001`:
|
||||
|
||||
```python
|
||||
# Incoming message: /state motorIndex frequency
|
||||
# Example: /state 1 100.0
|
||||
```
|
||||
|
||||
## Local Controls
|
||||
|
||||
Both the remote control and station controllers have a local interface:
|
||||
|
||||
### Rotary Encoder
|
||||
|
||||
| Action | Function |
|
||||
|--------|----------|
|
||||
| Rotate | Change frequency (or navigate menus) |
|
||||
| Press | Select siren / confirm selection |
|
||||
| Double-press (remote) | Kill all sirens |
|
||||
|
||||
### OLED Display
|
||||
|
||||
- Shows current frequency
|
||||
- Shows selected siren number
|
||||
- Shows connection status (WiFi)
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
sirens/
|
||||
├── README.md # This file
|
||||
├── AGENTS.md # Developer notes for AI agents
|
||||
├── src/ # Current firmware (v2)
|
||||
│ ├── siren_remote_control_esp32_v2/ # Remote control unit
|
||||
│ ├── siren_controller_esp32_*/ # Station controller
|
||||
│ └── B_G431B_ESC1_motor_controller/ # Motor controller (SimpleFOC)
|
||||
├── v1/ # Legacy hardware and firmware
|
||||
│ ├── src/ # Old versions of controller code
|
||||
│ └── designs/ # Legacy CAD files (FreeCAD)
|
||||
├── designs/ # Current CAD files
|
||||
│ ├── openscad/ # OpenSCAD source files
|
||||
│ │ ├── siren_redesign.scad # Main siren design
|
||||
│ │ └── box_snap.scad # Controller box
|
||||
│ ├── stl/ # 3D print files
|
||||
│ └── 3mf/ # 3MF print files
|
||||
```
|
||||
|
||||
## CAD Files
|
||||
|
||||
- **Main siren**: `designs/openscad/siren_redesign.scad`
|
||||
- **Controller box**: `designs/openscad/box_snap.scad`
|
||||
- **STL files**: `designs/stl/`
|
||||
|
||||
## Building & Flashing
|
||||
|
||||
Uses Arduino framework. For development, [PlatformIO](https://platformio.org/) is recommended for command-line builds.
|
||||
|
||||
See `AGENTS.md` for details on libraries and coding conventions.
|
||||
|
||||
## Version History
|
||||
|
||||
| Version | Description |
|
||||
|---------|-------------|
|
||||
| v1 | Legacy system (FreeCAD) |
|
||||
| v2 (current) | Programmatic CAD design (OpenSCAD), improved code structure |
|
||||
BIN
designs/3mf/base.3mf
Normal file
BIN
designs/3mf/base.3mf
Normal file
Binary file not shown.
BIN
designs/3mf/hub.3mf
Normal file
BIN
designs/3mf/hub.3mf
Normal file
Binary file not shown.
BIN
designs/3mf/hub_creality.3mf
Normal file
BIN
designs/3mf/hub_creality.3mf
Normal file
Binary file not shown.
BIN
designs/3mf/leg.3mf
Normal file
BIN
designs/3mf/leg.3mf
Normal file
Binary file not shown.
BIN
designs/3mf/leg_creality.3mf
Normal file
BIN
designs/3mf/leg_creality.3mf
Normal file
Binary file not shown.
BIN
designs/3mf/leg_top_creality.3mf
Normal file
BIN
designs/3mf/leg_top_creality.3mf
Normal file
Binary file not shown.
BIN
designs/3mf/motor_frame.3mf
Normal file
BIN
designs/3mf/motor_frame.3mf
Normal file
Binary file not shown.
BIN
designs/3mf/rotor.3mf
Normal file
BIN
designs/3mf/rotor.3mf
Normal file
Binary file not shown.
BIN
designs/3mf/rotor_creality.3mf
Normal file
BIN
designs/3mf/rotor_creality.3mf
Normal file
Binary file not shown.
BIN
designs/3mf/stator.3mf
Normal file
BIN
designs/3mf/stator.3mf
Normal file
Binary file not shown.
BIN
designs/3mf/stator_bottom.3mf
Normal file
BIN
designs/3mf/stator_bottom.3mf
Normal file
Binary file not shown.
BIN
designs/3mf/stator_creality.3mf
Normal file
BIN
designs/3mf/stator_creality.3mf
Normal file
Binary file not shown.
BIN
designs/3mf/stator_top.3mf
Normal file
BIN
designs/3mf/stator_top.3mf
Normal file
Binary file not shown.
BIN
designs/3mf/stator_top_creality.3mf
Normal file
BIN
designs/3mf/stator_top_creality.3mf
Normal file
Binary file not shown.
331
designs/openscad/box_snap.scad
Normal file
331
designs/openscad/box_snap.scad
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
include <Round-Anything-master/polyround.scad>
|
||||
|
||||
$fn = $preview ? 32 : 128;
|
||||
|
||||
module counter_bored_holes(screw_offset){
|
||||
// Subtract mounting holes for stator
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
// Subtract screw hole
|
||||
rotate([0, 0, i])
|
||||
translate([screw_offset, 0, -1]) // Adjusted position
|
||||
cylinder(d = screw_diameter, h = stator_wall_thickness + 2);
|
||||
|
||||
// Subtract counterbore
|
||||
rotate([0, 0, i])
|
||||
translate([screw_offset, 0, stator_wall_thickness - screw_bore_depth]) // Adjusted position
|
||||
cylinder(d = screw_bore_diameter, h = stator_wall_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
module rounded_cube(width, length, height, radius1, radius2) {
|
||||
polyRoundExtrude([
|
||||
[-(width + radius*2)/2, -(length + radius*2)/2, radius], // Bottom-left point
|
||||
[-(width + radius*2)/2, (length + radius*2)/2, radius], // Top-left point
|
||||
[(width + radius*2)/2, (length + radius*2)/2, radius], // Top-right point
|
||||
[(width + radius*2)/2, -(length + radius*2)/2, radius] // Bottom-right point
|
||||
], height + radius*2, radius1, radius2);
|
||||
}
|
||||
|
||||
module box() {
|
||||
padding = radius*2 + inset_distance*2;
|
||||
difference() {
|
||||
translate([0, 0, -(height + padding + wall_thickness*2)/2])
|
||||
rounded_cube(
|
||||
width + wall_thickness*2 + inset_distance*2,
|
||||
depth + wall_thickness*2 + inset_distance*2,
|
||||
height + wall_thickness*2 + inset_distance*2,
|
||||
radius, radius);
|
||||
|
||||
translate([0, 0, -(height + radius*2 + inset_distance*2)/2])
|
||||
rounded_cube(
|
||||
width + inset_distance*2,
|
||||
depth + inset_distance*2,
|
||||
height + inset_distance*2,
|
||||
radius, radius);
|
||||
|
||||
cube([width, depth, height], center = true);
|
||||
|
||||
translate([0, 0, (height + padding)/2 + inset_offset])
|
||||
rounded_inset(width = width, height = depth, inset_scale=0, hole = true, cutout = false);
|
||||
|
||||
rotate([180, 0, 0])
|
||||
translate([0, 0, (height + padding)/2 + inset_offset])
|
||||
rounded_inset(width = width, height = depth, cutout = false);
|
||||
|
||||
rotate([90, 90, 0])
|
||||
translate([0, 0, (depth + padding)/2 + inset_offset])
|
||||
rounded_inset(width = height, height = width, cutout = false);
|
||||
|
||||
rotate([-90, 90, 0])
|
||||
translate([0, 0, (depth + padding)/2 + inset_offset])
|
||||
rounded_inset(width = height, height = width, cutout = false);
|
||||
|
||||
rotate([0, 90, 0])
|
||||
translate([0, 0, (width + padding)/2 + inset_offset])
|
||||
rounded_inset(width = height, height = depth, cutout = false);
|
||||
|
||||
rotate([0, -90, 0])
|
||||
translate([0, 0, (width + padding)/2 + inset_offset])
|
||||
rounded_inset(width = height, height = depth, cutout = false);
|
||||
|
||||
/*
|
||||
translate([(width + padding)/2 + 1.5, (depth + padding)/2 + 1.5, 0])
|
||||
rotate([0, 0, 45])
|
||||
dowel();
|
||||
|
||||
rotate([180, 0, 0])
|
||||
translate([(width + padding)/2 + 1.5, (depth + padding)/2 + 1.5, 0])
|
||||
rotate([0, 0, 45])
|
||||
dowel();
|
||||
|
||||
rotate([0, 0, 90])
|
||||
translate([(depth + padding)/2 + 1.5, (width + padding)/2 + 1.5, 0])
|
||||
rotate([0, 0, 45])
|
||||
dowel();
|
||||
|
||||
rotate([0, 180, 90])
|
||||
translate([(depth + padding)/2 + 1.5, (width + padding)/2 + 1.5, 0])
|
||||
rotate([0, 0, 45])
|
||||
dowel();
|
||||
*/
|
||||
|
||||
translate([(width + padding + 3.5)/2, (depth + padding + 3.5)/2, (height + padding)/2 + wall_thickness])
|
||||
counter_bored_hole();
|
||||
|
||||
rotate([0, 0, 180])
|
||||
translate([(width + padding + 3.5)/2, (depth + padding + 3.5)/2, (height + padding)/2 + wall_thickness])
|
||||
counter_bored_hole();
|
||||
|
||||
rotate([0, 0, 90])
|
||||
translate([(depth + padding + 3.5)/2, (width + padding + 3.5)/2, (height + padding)/2 + wall_thickness])
|
||||
counter_bored_hole();
|
||||
|
||||
rotate([0, 0, -90])
|
||||
translate([(depth + padding + 3.5)/2, (width + padding + 3.5)/2, (height + padding)/2 + wall_thickness])
|
||||
counter_bored_hole();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module rounded_inset(width, height, inset_scale=0, hole = false, cutout = true) {
|
||||
|
||||
difference(){
|
||||
union(){
|
||||
translate([0, 0, inset_thickness/2 - 0.75])
|
||||
rounded_cube(
|
||||
width + 0.85 + inset_scale*2,
|
||||
height * 0.75 + inset_scale*2,
|
||||
1.5 - radius*2, 0.75, 0.75);
|
||||
|
||||
//translate([0, 0, -inset_thickness])
|
||||
rounded_cube(
|
||||
width + inset_scale*2,
|
||||
height + inset_scale*2,
|
||||
inset_thickness - radius*2 + inset_scale, 0, 2);
|
||||
|
||||
translate([0, 0, -2.5 + inset_scale])
|
||||
rounded_cube(
|
||||
width - radius*2 + inset_scale*2,
|
||||
height - radius*2 + inset_scale*2,
|
||||
2.5 - radius*2 - inset_scale, 0, 0);
|
||||
}
|
||||
|
||||
if(cutout)
|
||||
translate([0, 0, -2.5 + inset_scale])
|
||||
rounded_cube(
|
||||
width - radius*2 + inset_scale*2 - 2,
|
||||
height - radius*2 + inset_scale*2 - 2,
|
||||
2.5 - radius*2 - inset_scale, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
if(hole)
|
||||
translate([width/2 + 1, 0, -100])
|
||||
rounded_cube(2, 3, 110, 0, 0);
|
||||
|
||||
if(hole)
|
||||
rotate([0, 0, 180])
|
||||
translate([width/2 + 1, 0, -100])
|
||||
rounded_cube(2, 3, 110, 0, 0);
|
||||
}
|
||||
|
||||
module dowel(inset_scale=0){
|
||||
//cube([3 + inset_scale, 3 + inset_scale, 10 + inset_scale*2], center=true);
|
||||
translate([0, 0, -6])
|
||||
rounded_cube(-0.5 + inset_scale*3, -0.5 + inset_scale*3, 8 + inset_scale*3, 1, 1);
|
||||
}
|
||||
|
||||
module counter_bored_hole(){
|
||||
translate([0, 0, -7])
|
||||
cylinder(d = screw_bore_diameter, h = 7);
|
||||
translate([0, 0, -20])
|
||||
cylinder(d = screw_diameter, h = 20);
|
||||
translate([0, 0, -20])
|
||||
cylinder(d = screw_insert_diameter, h = 9);
|
||||
}
|
||||
|
||||
|
||||
module box_bottom() {
|
||||
difference(){
|
||||
intersection(){
|
||||
box();
|
||||
translate([0, 0, 40])
|
||||
cube([width + wall_thickness*2 + radius*4 + inset_distance*2,
|
||||
depth + wall_thickness*2 + radius*4 + inset_distance*2,
|
||||
height + wall_thickness*2 + radius*4 + inset_distance*2], center = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module box_top() {
|
||||
difference(){
|
||||
difference(){
|
||||
box();
|
||||
translate([0, 0, 40])
|
||||
cube([width + wall_thickness*2 + radius*4 + inset_distance*2,
|
||||
depth + wall_thickness*2 + radius*4 + inset_distance*2,
|
||||
height + wall_thickness*2 + radius*4 + inset_distance*2], center = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module front_face() {
|
||||
difference(){
|
||||
union(){
|
||||
rounded_inset(width = height, height = width, inset_scale=inset_scale_factor, hole = false);
|
||||
translate([0, 15, -7])
|
||||
cylinder(d = 18, h = 9.2);
|
||||
}
|
||||
|
||||
translate([0, 15, -5])
|
||||
cylinder(d = 16, h = 7.2);
|
||||
|
||||
translate([0, 15, -7])
|
||||
cylinder(d = 7.5, h = 7.5);
|
||||
|
||||
translate([0, -15, 0])
|
||||
linear_extrude(2.5){
|
||||
square([17.5, 25.5], center = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module back_face() {
|
||||
difference(){
|
||||
rounded_inset(width = height, height = width, inset_scale=inset_scale_factor, hole = false);
|
||||
|
||||
translate([0, -30, -10])
|
||||
cylinder(d = 10, h = 20);
|
||||
|
||||
translate([0, 30, -10])
|
||||
cylinder(d = 10, h = 20);
|
||||
|
||||
//rotate([0, 0, 90])
|
||||
translate([0, width/2 - 27.5, 0])
|
||||
for (i = [0:1:2]) {
|
||||
translate([0, -10 * i, -1])
|
||||
rounded_cube(15, -1, 10, 0, 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module bottom_face() {
|
||||
rounded_inset(width = width, height = depth, inset_scale=inset_scale_factor, hole = false);
|
||||
}
|
||||
|
||||
module bottom_face_alt() {
|
||||
tab_height = 35;
|
||||
tab_width = 65;
|
||||
rail_width = 48.5;
|
||||
difference(){
|
||||
union(){
|
||||
rounded_inset(width = width, height = depth, inset_scale=inset_scale_factor, hole = false);
|
||||
|
||||
rotate([180, 0, 0])
|
||||
translate([0, depth/2 - 20, 0])
|
||||
rounded_cube(tab_width, -1, tab_height, 2, -2);
|
||||
|
||||
rotate([180, 0, 0])
|
||||
translate([0, depth/2 - depth + 15, 0])
|
||||
rounded_cube(tab_width - 15, -1, tab_height, 2, -2);
|
||||
}
|
||||
|
||||
translate([0, -10, -tab_height + 3])
|
||||
cube([rail_width, 20, 3.5], center=true);
|
||||
|
||||
translate([0, -10, -tab_height + 3 + 26])
|
||||
cube([rail_width, 20, 3.5], center=true);
|
||||
|
||||
translate([-tab_width + 7 + 10, -10, -tab_height + 3 + 13])
|
||||
cube([rail_width, 20, 13], center=true);
|
||||
|
||||
translate([0, 10, -tab_height + 3 + 17])
|
||||
cube([35, 20, 13], center=true);
|
||||
|
||||
//translate([0, 10, -tab_height + 3])
|
||||
// cube([35, 20, 6], center=true);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module top_face() {
|
||||
difference(){
|
||||
rounded_inset(width = width, height = depth, inset_scale=inset_scale_factor, hole = false);
|
||||
|
||||
rotate([0, 0, 90])
|
||||
translate([depth/2 - 15, width/2 - 12.5, -5])
|
||||
for (i = [0:1:5]) {
|
||||
translate([0, -10 * i, -1])
|
||||
rounded_cube(15, -1, 10, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module side_face() {
|
||||
rounded_inset(width = height, height = depth, inset_scale=inset_scale_factor, hole = false);
|
||||
}
|
||||
|
||||
width = 75;//90; // Total width
|
||||
height = 30; // Total height
|
||||
depth = 60; //110; // Total depth
|
||||
radius = 2; // Box corner radius
|
||||
wall_thickness = 7; // Wall thickness
|
||||
inset_distance = 1; //distance from inset side
|
||||
inset_offset = 4.5;
|
||||
inset_thickness = 2.5;
|
||||
inset_scale_factor = -0.3;
|
||||
|
||||
screw_diameter = 3.2; // Diameter of screws - M3 clearance hole (3.2mm diameter)
|
||||
screw_bore_diameter = 6.5;
|
||||
screw_insert_diameter = 4.5;
|
||||
|
||||
//
|
||||
difference(){
|
||||
union(){
|
||||
//box_top();
|
||||
//rotate([180, 0, 0])
|
||||
//half_box();
|
||||
//translate([0, 0, (height + radius*2 + inset_distance*2)/2 + inset_offset])
|
||||
//rounded_inset(width = width, height = depth, inset_scale=inset_scale_factor, hole = false);
|
||||
//padding = radius*2 + inset_distance*2;
|
||||
//rotate([0, 0, 90])
|
||||
//translate([(depth + padding)/2 + inset_offset*1.5, (width + padding)/2 + inset_offset*1.5, 0])
|
||||
//rotate([0, 0, 45])
|
||||
//dowel(inset_scale=inset_scale_factor);
|
||||
}
|
||||
//cube([100, 100, 100]);
|
||||
}
|
||||
|
||||
//padding = radius*2 + inset_distance*2;
|
||||
//translate([(width + padding + 3)/2 + 5, (depth + padding + 3)/2, (height + padding)/2 + wall_thickness])
|
||||
// counter_bored_hole();
|
||||
//rounded_inset(width = height, height = width, cutout = true);
|
||||
|
||||
//front_face();
|
||||
//bottom_face_alt();
|
||||
side_face();
|
||||
491
designs/openscad/siren_redesign.scad
Normal file
491
designs/openscad/siren_redesign.scad
Normal file
|
|
@ -0,0 +1,491 @@
|
|||
/* [Siren General Parameters] */
|
||||
siren_diameter = 125; // Base diameter - 121 for rotor to fit original siren
|
||||
siren_height = 45; // Height of the rotor - 60 for rotor to fit original siren
|
||||
number_of_ports = 3; // Number of ports
|
||||
vertical_tolerance = 2; // Tolerance between stator and rotor
|
||||
radial_tolerance = 1.5;
|
||||
fit_reduction = 0.2;
|
||||
pressure_zone = 0.5; // Percentage of diameter
|
||||
round_radius = 4; // Rounding of top/bottom
|
||||
$fn = $preview ? 32 : 128; // Number of fragments
|
||||
|
||||
/* [Stator Parameters] */
|
||||
stator_wall_thickness = 8; // Stator wall thickness
|
||||
number_of_mounting_holes = 4; // Number of mounting holes
|
||||
screw_diameter = 3.2; // Diameter of screws - M3 clearance hole (3.2mm diameter)
|
||||
screw_bore_diameter = 6.5;
|
||||
screw_insert_diameter = 4.5;
|
||||
screw_bore_depth = 2.5;
|
||||
screw_length = 10 - (stator_wall_thickness - screw_bore_depth);
|
||||
stator_screw_offset = siren_diameter/2 - stator_wall_thickness/2; //distance to screws
|
||||
|
||||
/* [Rotor Parameters] */
|
||||
rotor_wall_thickness = 3; // Rotor wall thickness
|
||||
hub_height = 3;
|
||||
hub_diameter = 12;
|
||||
blade_angle = 8;
|
||||
rotor_height = siren_height - (vertical_tolerance * 2);
|
||||
port_height = siren_height - (rotor_wall_thickness * 2) - (vertical_tolerance * 2); // Height of the port
|
||||
|
||||
/* [Motor Parameters] */
|
||||
motor_shaft_diameter = 8; // Diameter of the motor body
|
||||
motor_frame_diameter = 60;
|
||||
motor_diameter = 28; // Diameter of the motor body
|
||||
motor_height = 24;
|
||||
magnet_diameter = 7;
|
||||
motor_screw_x_offset = 9.5;
|
||||
motor_screw_y_offset = 8;
|
||||
motor_screw_offset = 8;
|
||||
motor_frame_screw_offset = motor_frame_diameter/2 - stator_wall_thickness/2; //distance to screws
|
||||
|
||||
/* [Base Parameters] */
|
||||
leg_screw_offset = motor_frame_diameter / 2 + 5;
|
||||
leg_height = motor_height + 10;
|
||||
|
||||
// helper functions
|
||||
module ports(diameter, height, ports, r_offset = 0) {
|
||||
radius = diameter / 2;
|
||||
angle = (180 / ports) - (r_offset * 6);
|
||||
|
||||
rotate([0, 0, 180 / ports])
|
||||
for (i = [0:360/ports:360]) {
|
||||
rotate([0, 0, i + (r_offset * 6 / 2)])
|
||||
linear_extrude(height)
|
||||
polygon(concat(
|
||||
[[0, 0]], // Center point
|
||||
[for (j = [0 : $fn])
|
||||
[radius * cos(angle * j / $fn),
|
||||
radius * sin(angle * j / $fn)]]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
module column(outer_diameter, inner_diameter, height, z_offset = 0) {
|
||||
difference(){
|
||||
// Outer cylinder
|
||||
cylinder(d = outer_diameter, h = height);
|
||||
// Subtract inner cylinder
|
||||
translate([0, 0, z_offset])
|
||||
cylinder(d = inner_diameter, h = height - z_offset);
|
||||
}
|
||||
}
|
||||
|
||||
module rounded_column(outer_diameter, inner_diameter, height, z_offset = 0){
|
||||
difference() {
|
||||
// Rounded top
|
||||
intersection() {
|
||||
// Unrounded full cylinder
|
||||
cylinder(d = outer_diameter, h = height);
|
||||
|
||||
// Add Minkowski rounded version
|
||||
minkowski() {
|
||||
cylinder(h = height - round_radius, d = outer_diameter - round_radius * 2);
|
||||
sphere(r = round_radius);
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract inner cylinder
|
||||
translate([0, 0, z_offset])
|
||||
cylinder(d = inner_diameter, h = height);
|
||||
}
|
||||
}
|
||||
|
||||
module counter_bored_holes(screw_offset){
|
||||
// Subtract mounting holes for stator
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
// Subtract screw hole
|
||||
rotate([0, 0, i])
|
||||
translate([screw_offset, 0, -1]) // Adjusted position
|
||||
cylinder(d = screw_diameter, h = stator_wall_thickness + 2);
|
||||
|
||||
// Subtract counterbore
|
||||
rotate([0, 0, i])
|
||||
translate([screw_offset, 0, stator_wall_thickness - screw_bore_depth]) // Adjusted position
|
||||
cylinder(d = screw_bore_diameter, h = stator_wall_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
module mounting_holes(screw_offset){
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
rotate([0, 0, i])
|
||||
translate([screw_offset, 0, 0])
|
||||
cylinder(d = screw_insert_diameter, h = screw_length);
|
||||
}
|
||||
}
|
||||
|
||||
// Linear Interpolation
|
||||
function lerp(a, b, t) = a * (1 - t) + b * t;
|
||||
|
||||
// Arc between points function
|
||||
function arc_between_points(p1, p2, height, steps=20) =
|
||||
[for(t = [0:1/steps:1])
|
||||
let(
|
||||
x = lerp(p1.x, p2.x, t),
|
||||
y = lerp(p1.y, p2.y, t),
|
||||
// Parabolic arc height calculation
|
||||
arc_height = height * (1 - pow(2*t-1, 2))
|
||||
)
|
||||
[x, y + arc_height]
|
||||
];
|
||||
|
||||
function bezier_curve(t, p0, p1, p2, p3, p4) =
|
||||
pow(1-t, 4) * p0 +
|
||||
4 * pow(1-t, 3) * t * p1 +
|
||||
6 * pow(1-t, 2) * pow(t, 2) * p2 +
|
||||
4 * (1-t) * pow(t, 3) * p3 +
|
||||
pow(t, 4) * p4;
|
||||
|
||||
function curved_polygon(points, steps, x_shift, y_shift) =
|
||||
let(
|
||||
left_curve = [for (t = [0 : 1/steps : 1])
|
||||
bezier_curve(t, points[0], points[1], points[2], points[3], points[4])
|
||||
],
|
||||
right_curve = [for (pt = left_curve)
|
||||
[pt[0] + x_shift, pt[1] + y_shift]
|
||||
]
|
||||
)
|
||||
concat(left_curve, [for (i = [len(right_curve)-1 : -1 : 0]) right_curve[i]]);
|
||||
|
||||
module stator() {
|
||||
difference() {
|
||||
|
||||
// Create column
|
||||
column(siren_diameter, siren_diameter - (stator_wall_thickness * 2), siren_height);
|
||||
|
||||
// Subtract ports
|
||||
translate([0, 0, (siren_height - port_height) / 2])
|
||||
ports(siren_diameter, port_height, number_of_ports);
|
||||
|
||||
mounting_holes(stator_screw_offset);
|
||||
|
||||
translate([0, 0, siren_height - screw_length])
|
||||
mounting_holes(stator_screw_offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module impeller_blade(radius, rotor_height){
|
||||
// Generate points
|
||||
points1 = arc_between_points([0, -hub_diameter / 2], [radius, 0], -blade_angle);
|
||||
points2 = [for(p = [len(points1)-1:-1:0]) points1[p] - [0, -rotor_wall_thickness]];
|
||||
|
||||
// Create the blade by combining top and bottom points
|
||||
linear_extrude(height = rotor_height)
|
||||
polygon(concat(points1, points2));
|
||||
}
|
||||
|
||||
module impeller_blades_curved(radius, rotor_height){
|
||||
difference() {
|
||||
union() {
|
||||
// Rotate and create blades
|
||||
for (i = [0:360/number_of_ports:360]) {
|
||||
rotate([0, 0, i])
|
||||
impeller_blade(radius, rotor_height);
|
||||
}
|
||||
}
|
||||
|
||||
top_rounding_radius = 5;
|
||||
bottom_rounding_radius = rotor_height / 2 - 1;
|
||||
//bottom_rounding_radius = rotor_height / 2 - 10; // this needs to be adjusted based on the rotor diameter and height
|
||||
top_diameter = (siren_diameter * pressure_zone) + (top_rounding_radius * 2);
|
||||
bottom_diameter = (siren_diameter * pressure_zone) - (2 * bottom_rounding_radius);
|
||||
top_height = top_rounding_radius + 10;
|
||||
bottom_height = rotor_height - bottom_rounding_radius - (top_rounding_radius * 2) + 2;
|
||||
translate([0, 0, rotor_height - top_rounding_radius])
|
||||
union(){
|
||||
difference() {
|
||||
// Column
|
||||
cylinder(d = top_diameter, h = top_height);
|
||||
|
||||
// Top torus
|
||||
rotate_extrude()
|
||||
translate([top_diameter / 2, 0, 0])
|
||||
circle(r = top_rounding_radius);
|
||||
}
|
||||
|
||||
rotate([180, 0, 0])
|
||||
minkowski() {
|
||||
// Original cylinder, reduced by twice the rounding radius
|
||||
cylinder(d = bottom_diameter, h = bottom_height);
|
||||
|
||||
// Sphere for rounding
|
||||
sphere(r = bottom_rounding_radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module rotor() {
|
||||
rotor_diameter = siren_diameter - (stator_wall_thickness * 2) - (radial_tolerance * 2);
|
||||
rotor_inner_diameter = rotor_diameter - (rotor_wall_thickness * 2);
|
||||
|
||||
difference() {
|
||||
union(){
|
||||
difference() {
|
||||
column(rotor_diameter, rotor_inner_diameter, rotor_height, rotor_wall_thickness);
|
||||
|
||||
// Subtract ports
|
||||
translate([0, 0, (rotor_height - port_height) / 2])
|
||||
ports(rotor_diameter, port_height, number_of_ports);
|
||||
}
|
||||
|
||||
// Add blades
|
||||
impeller_blades_curved(rotor_inner_diameter / 2, rotor_height);
|
||||
}
|
||||
|
||||
translate([0, 0, 0])
|
||||
hub_attachment(fit_reduction);
|
||||
}
|
||||
}
|
||||
|
||||
module hub(aug = 0) {
|
||||
height = (rotor_wall_thickness * 2) + hub_height;
|
||||
difference(){
|
||||
union(){
|
||||
hub_attachment(aug);
|
||||
cylinder(d = motor_shaft_diameter + 2, h = height);
|
||||
}
|
||||
|
||||
intersection(){
|
||||
cylinder(d = motor_shaft_diameter, h = height);
|
||||
cube([motor_shaft_diameter, motor_shaft_diameter-1, height * 2], center = true);
|
||||
};
|
||||
|
||||
cylinder(d = motor_shaft_diameter, h = 2);
|
||||
}
|
||||
}
|
||||
|
||||
module hub_attachment(aug = 0) {
|
||||
insert_width = 2;
|
||||
outer_diameter = hub_diameter + (insert_width * 6) + (aug * 2);
|
||||
mid_diameter = hub_diameter + (insert_width * 2) + (aug * 2);
|
||||
inner_diameter = outer_diameter - (insert_width * 2) - (aug * 4);
|
||||
difference(){
|
||||
column(outer_diameter, inner_diameter, rotor_wall_thickness);
|
||||
//translate([0, 0, -10])
|
||||
ports(outer_diameter, rotor_wall_thickness, number_of_ports, aug * 2);
|
||||
}
|
||||
translate([0, 0, rotor_wall_thickness])
|
||||
cylinder(d = outer_diameter, rotor_wall_thickness);
|
||||
cylinder(d = mid_diameter, rotor_wall_thickness * 2);
|
||||
}
|
||||
|
||||
module stator_top() {
|
||||
difference(){
|
||||
rounded_column(
|
||||
siren_diameter,
|
||||
siren_diameter * pressure_zone,
|
||||
stator_wall_thickness
|
||||
);
|
||||
|
||||
counter_bored_holes(stator_screw_offset);
|
||||
}
|
||||
}
|
||||
|
||||
module stator_bottom() {
|
||||
//rotate([180, 0, 0])
|
||||
difference() {
|
||||
rounded_column(
|
||||
siren_diameter,
|
||||
motor_diameter + (radial_tolerance * 2),
|
||||
stator_wall_thickness
|
||||
);
|
||||
|
||||
counter_bored_holes(stator_screw_offset);
|
||||
|
||||
// Subtract mounting holes for motor frame
|
||||
rotate([180, 0, 0])
|
||||
translate([0, 0, -stator_wall_thickness])
|
||||
counter_bored_holes(motor_frame_screw_offset);
|
||||
|
||||
rotate([180, 0, 0])
|
||||
translate([0, 0, -stator_wall_thickness])
|
||||
counter_bored_holes(leg_screw_offset);
|
||||
|
||||
translate([0, 0, stator_wall_thickness])
|
||||
rotate([0, 0, -45/2 - 0.6])
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
rotate([0, 0, i]) {
|
||||
leg(fit_reduction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module motor_frame() {
|
||||
//rotate([180, 0, 0])
|
||||
//translate([0, 0, -stator_wall_thickness])
|
||||
motor_frame_height = motor_height - stator_wall_thickness - vertical_tolerance;
|
||||
difference(){
|
||||
union(){
|
||||
inner_diameter = motor_frame_diameter - (stator_wall_thickness * 2);
|
||||
difference(){
|
||||
rounded_column(
|
||||
motor_frame_diameter,
|
||||
inner_diameter,
|
||||
motor_frame_height + stator_wall_thickness,
|
||||
-stator_wall_thickness
|
||||
);
|
||||
|
||||
translate([0, 0, stator_wall_thickness])
|
||||
ports(inner_diameter, motor_frame_height, number_of_mounting_holes);
|
||||
|
||||
//translate([0, 0, -stator_wall_thickness])
|
||||
//rotate([0, 0, -45/2])
|
||||
ports(motor_frame_diameter, motor_frame_height, number_of_mounting_holes);
|
||||
|
||||
rotate([0, 0, 45/2])
|
||||
mounting_holes(motor_frame_screw_offset);
|
||||
}
|
||||
|
||||
translate([0, 0, motor_frame_height])
|
||||
column(motor_screw_x_offset * 2 + (radial_tolerance * 2) , magnet_diameter + (radial_tolerance * 2), stator_wall_thickness);
|
||||
}
|
||||
|
||||
// Subtract mounting holes for motor
|
||||
translate([0, 0, motor_frame_height])
|
||||
rotate([0, 0, 45/2])
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
rotate([0, 0, i]) {
|
||||
// Use modulo to alternate between x and y offsets
|
||||
motor_screw_offset = (floor(i / (360/number_of_mounting_holes)) % 2 == 0) ?
|
||||
motor_screw_x_offset : motor_screw_y_offset;
|
||||
|
||||
translate([motor_screw_offset, 0, 0])
|
||||
cylinder(d = screw_diameter, h = stator_wall_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
translate([0, 0, motor_frame_height])
|
||||
cylinder(d = magnet_diameter + (radial_tolerance * 2), h = stator_wall_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
module leg(aug = 0) {
|
||||
motor_spacing = 15;
|
||||
|
||||
points = [
|
||||
[leg_screw_offset + 10, leg_height],
|
||||
[leg_screw_offset + 5, leg_height-5],
|
||||
[leg_screw_offset + 0, leg_height / 2],
|
||||
[leg_screw_offset + 5, 5],
|
||||
[leg_screw_offset + 10, 0]
|
||||
];
|
||||
|
||||
rotate_extrude(angle = 360/8) {
|
||||
polygon(curved_polygon(
|
||||
points,
|
||||
steps = 100,
|
||||
x_shift = -stator_wall_thickness / 2,
|
||||
y_shift = 0
|
||||
));
|
||||
}
|
||||
|
||||
rotate([0, 0, 12 - (aug / 2)])
|
||||
translate([0, 0, -1])
|
||||
difference(){
|
||||
rotate_extrude(angle = 360/16 + aug) {
|
||||
translate([leg_screw_offset - ((screw_insert_diameter + radial_tolerance) / 2) - aug, 0, 0])
|
||||
square([screw_insert_diameter + radial_tolerance + aug * 2, leg_height + 2]);
|
||||
}
|
||||
if(aug == 0){
|
||||
rotate([0, 0, 45/4 - aug])
|
||||
//translate([0, 0, -2])
|
||||
mounting_holes(leg_screw_offset);
|
||||
|
||||
rotate([0, 0, 45/4 - aug])
|
||||
translate([0, 0, leg_height - screw_length + 2])
|
||||
mounting_holes(leg_screw_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module base() {
|
||||
difference(){
|
||||
rounded_column(
|
||||
siren_diameter,
|
||||
(leg_screw_offset - ((screw_diameter + radial_tolerance) / 2)) * 2 - 5,
|
||||
stator_wall_thickness
|
||||
);
|
||||
|
||||
counter_bored_holes(stator_screw_offset);
|
||||
translate([0, 0, stator_wall_thickness])
|
||||
rotate([180, 0, 0])
|
||||
//counter_bored_holes(leg_screw_offset - ((screw_diameter + tolerance) / 2));
|
||||
//rotate([0, 0, -45/2])
|
||||
counter_bored_holes(leg_screw_offset);
|
||||
|
||||
translate([0, 0, stator_wall_thickness])
|
||||
rotate([0, 0, -45/2 - 0.6])
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
rotate([0, 0, i]) {
|
||||
leg(fit_reduction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//rotate([0, 0, 0])
|
||||
//translate([0, 0, 0])
|
||||
//rotor();
|
||||
//motor_shaft_diameter = 8.2;
|
||||
hub(0.1);
|
||||
//stator();
|
||||
//base();
|
||||
//stator_bottom();
|
||||
|
||||
//rotate([0, 0, 0])
|
||||
//translate([siren_diameter, 0, vertical_tolerance])
|
||||
//rotor();
|
||||
//stator_bottom();
|
||||
//rotate([0, 0, 67.5])
|
||||
//motor_frame();
|
||||
|
||||
//rotate([0, 0, 0])
|
||||
//translate([0, 0, siren_height + 2])
|
||||
//stator_top();
|
||||
|
||||
|
||||
//rotate([180, 0, 0])
|
||||
//translate([0, 0, 2])
|
||||
//stator_bottom();
|
||||
|
||||
|
||||
//rotate([180, 0, 45])
|
||||
//translate([0, 0, stator_wall_thickness + 5])
|
||||
//motor_frame();
|
||||
//leg();
|
||||
//base();
|
||||
/*
|
||||
rotate([0, 0, 0])
|
||||
translate([0, 0, -leg_height - 15 - stator_wall_thickness - 2])
|
||||
base();
|
||||
|
||||
rotate([0, 0, -45/2])
|
||||
translate([0, 0, -leg_height - 15])
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
rotate([0, 0, i]) {
|
||||
leg();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
//translate([0, 0, 2])
|
||||
intersection(){
|
||||
//color("red")
|
||||
rotor();
|
||||
cylinder(d = 30, h = 10);
|
||||
}
|
||||
|
||||
translate([040, 0, 0])
|
||||
hub();
|
||||
*/
|
||||
|
||||
|
||||
292
designs/openscad/stator_iris_top.scad
Normal file
292
designs/openscad/stator_iris_top.scad
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
/* [Siren General Parameters] */
|
||||
siren_diameter = 121; // Base diameter - 121 for rotor to fit original siren
|
||||
siren_height = 60; // Height of the rotor - 60 for rotor to fit original siren
|
||||
number_of_ports = 6; // Number of ports
|
||||
tolerance = 2; // Tolerance between stator and rotor
|
||||
pressure_zone = 0.5; // Percentage of diameter
|
||||
round_radius = 4; // Rounding of top/bottom
|
||||
$fn = $preview ? 32 : 128; // Number of fragments
|
||||
|
||||
/* [Stator Parameters] */
|
||||
stator_wall_thickness = 8; // Stator wall thickness
|
||||
number_of_mounting_holes = 4; // Number of mounting holes
|
||||
screw_diameter = 3.2; // Diameter of screws - M3 clearance hole (3.2mm diameter)
|
||||
screw_bore_diameter = 6.5;
|
||||
screw_insert_diameter = 4.5;
|
||||
screw_bore_depth = 2.5;
|
||||
screw_length = 10 - (stator_wall_thickness - screw_bore_depth);
|
||||
stator_screw_offset = siren_diameter/2 - stator_wall_thickness/2; //distance to screws
|
||||
|
||||
/* [Rotor Parameters] */
|
||||
rotor_wall_thickness = 3; // Rotor wall thickness
|
||||
hub_height = 3;
|
||||
hub_diameter = 12;
|
||||
blade_angle = 8;
|
||||
port_height = siren_height - (rotor_wall_thickness * 2) - (tolerance * 2); // Height of the port
|
||||
|
||||
/* [Motor Parameters] */
|
||||
motor_shaft_diameter = 8; // Diameter of the motor body
|
||||
motor_frame_diameter = 60;
|
||||
motor_diameter = 28; // Diameter of the motor body
|
||||
motor_height = 24;
|
||||
magnet_diameter = 7;
|
||||
motor_screw_x_offset = 9.5;
|
||||
motor_screw_y_offset = 8;
|
||||
motor_screw_offset = 8;
|
||||
motor_frame_screw_offset = motor_frame_diameter/2 - stator_wall_thickness/2; //distance to screws
|
||||
|
||||
/* [Base Parameters] */
|
||||
leg_screw_offset = motor_frame_diameter / 2 + 5;
|
||||
leg_height = motor_height + 10;
|
||||
|
||||
/* [Iris Parameters] */
|
||||
iris_blade_arc_angle = 135;
|
||||
number_of_iris_blades = 8;
|
||||
|
||||
// helper functions
|
||||
module ports(diameter, height, ports, r_offset = 0) {
|
||||
radius = diameter / 2;
|
||||
angle = (180 / ports) - (r_offset * 6);
|
||||
|
||||
rotate([0, 0, 180 / ports])
|
||||
for (i = [0:360/ports:360]) {
|
||||
rotate([0, 0, i + (r_offset * 6 / 2)])
|
||||
linear_extrude(height)
|
||||
polygon(concat(
|
||||
[[0, 0]], // Center point
|
||||
[for (j = [0 : $fn])
|
||||
[radius * cos(angle * j / $fn),
|
||||
radius * sin(angle * j / $fn)]]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
module column(outer_diameter, inner_diameter, height, z_offset = 0) {
|
||||
difference(){
|
||||
// Outer cylinder
|
||||
cylinder(d = outer_diameter, h = height);
|
||||
// Subtract inner cylinder
|
||||
translate([0, 0, z_offset])
|
||||
cylinder(d = inner_diameter, h = height - z_offset);
|
||||
}
|
||||
}
|
||||
|
||||
module rounded_column(outer_diameter, inner_diameter, height, z_offset = 0){
|
||||
difference() {
|
||||
// Rounded top
|
||||
intersection() {
|
||||
// Unrounded full cylinder
|
||||
cylinder(d = outer_diameter, h = height);
|
||||
|
||||
// Add Minkowski rounded version
|
||||
minkowski() {
|
||||
cylinder(h = height - round_radius, d = outer_diameter - round_radius * 2);
|
||||
sphere(r = round_radius);
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract inner cylinder
|
||||
translate([0, 0, z_offset])
|
||||
cylinder(d = inner_diameter, h = height);
|
||||
}
|
||||
}
|
||||
|
||||
module counter_bored_holes(screw_offset){
|
||||
// Subtract mounting holes for stator
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
// Subtract screw hole
|
||||
rotate([0, 0, i])
|
||||
translate([screw_offset, 0, -1]) // Adjusted position
|
||||
cylinder(d = screw_diameter, h = stator_wall_thickness + 2);
|
||||
|
||||
// Subtract counterbore
|
||||
rotate([0, 0, i])
|
||||
translate([screw_offset, 0, stator_wall_thickness - screw_bore_depth]) // Adjusted position
|
||||
cylinder(d = screw_bore_diameter, h = stator_wall_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
module mounting_holes(screw_offset){
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
rotate([0, 0, i])
|
||||
translate([screw_offset, 0, 0])
|
||||
cylinder(d = screw_insert_diameter, h = screw_length);
|
||||
}
|
||||
}
|
||||
|
||||
// Linear Interpolation
|
||||
function lerp(a, b, t) = a * (1 - t) + b * t;
|
||||
|
||||
// Arc between points function
|
||||
function arc_between_points(p1, p2, height, steps=20) =
|
||||
[for(t = [0:1/steps:1])
|
||||
let(
|
||||
x = lerp(p1.x, p2.x, t),
|
||||
y = lerp(p1.y, p2.y, t),
|
||||
// Parabolic arc height calculation
|
||||
arc_height = height * (1 - pow(2*t-1, 2))
|
||||
)
|
||||
[x, y + arc_height]
|
||||
];
|
||||
|
||||
function bezier_curve(t, p0, p1, p2, p3, p4) =
|
||||
pow(1-t, 4) * p0 +
|
||||
4 * pow(1-t, 3) * t * p1 +
|
||||
6 * pow(1-t, 2) * pow(t, 2) * p2 +
|
||||
4 * (1-t) * pow(t, 3) * p3 +
|
||||
pow(t, 4) * p4;
|
||||
|
||||
function curved_polygon(points, steps, x_shift, y_shift) =
|
||||
let(
|
||||
left_curve = [for (t = [0 : 1/steps : 1])
|
||||
bezier_curve(t, points[0], points[1], points[2], points[3], points[4])
|
||||
],
|
||||
right_curve = [for (pt = left_curve)
|
||||
[pt[0] + x_shift, pt[1] + y_shift]
|
||||
]
|
||||
)
|
||||
concat(left_curve, [for (i = [len(right_curve)-1 : -1 : 0]) right_curve[i]]);
|
||||
|
||||
module stator_top() {
|
||||
difference(){
|
||||
rounded_column(
|
||||
siren_diameter,
|
||||
siren_diameter * pressure_zone,
|
||||
stator_wall_thickness
|
||||
);
|
||||
|
||||
counter_bored_holes(stator_screw_offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module iris_top() {
|
||||
|
||||
outer_diameter = stator_screw_offset * 2 - 5;
|
||||
inner_diameter = siren_diameter * pressure_zone + 5;
|
||||
blade_height = 0.5;
|
||||
|
||||
center = (inner_diameter / 2) + ((outer_diameter - inner_diameter) / 4);
|
||||
|
||||
difference(){
|
||||
column(
|
||||
outer_diameter,
|
||||
inner_diameter,
|
||||
3
|
||||
);
|
||||
|
||||
translate([0, 0, 2])
|
||||
linear_extrude(){
|
||||
for (i = [0:360/8:360]) {
|
||||
rotate([0, 0, i])
|
||||
translate([center, 0, 0])
|
||||
rotate([0, 0, 90])
|
||||
square([5, 25], center = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
column(outer_diameter + 0.5, outer_diameter - 0.5, 5);
|
||||
}
|
||||
|
||||
module iris_bottom() {
|
||||
|
||||
outer_diameter = stator_screw_offset * 2 - 5;
|
||||
inner_diameter = siren_diameter * pressure_zone + 5;
|
||||
//blade_height = 0.5;
|
||||
|
||||
center = (inner_diameter / 2) + ((outer_diameter - inner_diameter) / 4);
|
||||
|
||||
difference(){
|
||||
column(
|
||||
siren_diameter,
|
||||
siren_diameter * pressure_zone,
|
||||
3
|
||||
);
|
||||
|
||||
translate([0, 0, 2])
|
||||
linear_extrude(){
|
||||
for (i = [0:360/8:360]) {
|
||||
rotate([0, 0, i])
|
||||
translate([center, 0, 0])
|
||||
circle(2.5);
|
||||
}
|
||||
}
|
||||
|
||||
counter_bored_holes(stator_screw_offset);
|
||||
|
||||
translate([0, 0, 1])
|
||||
column(outer_diameter + 1, outer_diameter - 1, 10);
|
||||
}
|
||||
}
|
||||
|
||||
module iris_blade() {
|
||||
outer_diameter = stator_screw_offset * 2 - 10;
|
||||
inner_diameter = siren_diameter * pressure_zone + 10;
|
||||
blade_height = 0.5;
|
||||
|
||||
center = (inner_diameter / 2) + ((outer_diameter - inner_diameter) / 4);
|
||||
|
||||
linear_extrude(blade_height){
|
||||
difference(){
|
||||
for (i = [0:iris_blade_arc_angle/$fn:iris_blade_arc_angle]) {
|
||||
rotate([0, 0, i])
|
||||
translate([center, 0, 0])
|
||||
circle((outer_diameter - inner_diameter) / 4);
|
||||
}
|
||||
|
||||
translate([center, 0, 0])
|
||||
circle(2.5);
|
||||
|
||||
rotate([0, 0, iris_blade_arc_angle])
|
||||
translate([center, 0, 0])
|
||||
circle(2.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//color("blue")
|
||||
iris_bottom();
|
||||
|
||||
translate([0, 0, 2])
|
||||
color("red")
|
||||
iris_blade();
|
||||
translate([0, 0, 10])
|
||||
iris_top();
|
||||
|
||||
|
||||
|
||||
/*
|
||||
iris_blade_arc_angle = 135;
|
||||
number_of_iris_blades = 8;
|
||||
|
||||
outer_diameter = stator_screw_offset * 2 - tolerance;
|
||||
inner_diameter = siren_diameter * pressure_zone + tolerance;
|
||||
center = (inner_diameter / 2) + ((outer_diameter - inner_diameter) / 4);
|
||||
|
||||
rotate([0, 0, 0])
|
||||
color("blue")
|
||||
square([5, 200]);
|
||||
//iris_top();
|
||||
rotate([0, 0, -45])
|
||||
for(i = [0:360/8:360/8 * 0]){
|
||||
//this point is the radial translation of the rh pin based on angle of the blade
|
||||
pin_angle = -70;
|
||||
echo(center * cos(pin_angle), center * sin(pin_angle));
|
||||
//echo((center * cos(pin_angle)) + pin_angle);
|
||||
//rotate([0, 0, -10])
|
||||
rotate([0, 0, i])
|
||||
translate([(center + 0) * cos(pin_angle), (center + 0) * sin(pin_angle), i/50])
|
||||
rotate([0, 0, -35])
|
||||
//rotate([0, 0, -0 + (pin_angle / ((iris_blade_arc_angle / -(pin_angle)) + 1))])
|
||||
translate([-center, 0, 0])
|
||||
|
||||
color("red")
|
||||
iris_blade();
|
||||
}
|
||||
translate([0, 0, -10])
|
||||
circle(d = center * 2);
|
||||
*/
|
||||
|
||||
|
||||
|
||||
36
designs/openscad/tests/bezier_tests.scad
Normal file
36
designs/openscad/tests/bezier_tests.scad
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
function bezier_curve(t, p0, p1, p2, p3, p4) =
|
||||
pow(1-t, 4) * p0 +
|
||||
4 * pow(1-t, 3) * t * p1 +
|
||||
6 * pow(1-t, 2) * pow(t, 2) * p2 +
|
||||
4 * (1-t) * pow(t, 3) * p3 +
|
||||
pow(t, 4) * p4;
|
||||
|
||||
function curved_polygon(points, steps, x_shift, y_shift) =
|
||||
let(
|
||||
left_curve = [for (t = [0 : 1/steps : 1])
|
||||
bezier_curve(t, points[0], points[1], points[2], points[3], points[4])
|
||||
],
|
||||
right_curve = [for (pt = left_curve)
|
||||
[pt[0] + x_shift, pt[1] + y_shift]
|
||||
]
|
||||
)
|
||||
concat(left_curve, [for (i = [len(right_curve)-1 : -1 : 0]) right_curve[i]]);
|
||||
|
||||
module base() {
|
||||
rotate_extrude() {
|
||||
polygon(curved_polygon(
|
||||
points = [
|
||||
[-80, 30],
|
||||
[-50, 15],
|
||||
[-50, 0],
|
||||
[-50, -15],
|
||||
[-80, -30]
|
||||
],
|
||||
steps = 100,
|
||||
x_shift = 50,
|
||||
y_shift = 0
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
base();
|
||||
79
designs/openscad/tests/blade_work.scad
Normal file
79
designs/openscad/tests/blade_work.scad
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// Blade Dimensions
|
||||
|
||||
diameter = 100; // Base diameter
|
||||
height = 100; // Height of the rotor
|
||||
wall_thickness = 6; // Wall thickness
|
||||
tolerance = 2; // Tolerance between stator and rotor
|
||||
number_of_ports = 6; // Number of ports [2:8]
|
||||
port_height = 50; // Height of the port
|
||||
pressure_zone = 0.4; // Define this globally with an appropriate default value
|
||||
mounting_hole_diameter = 4; // Diameter of mounting holes
|
||||
mounting_hole_offset = 20; // Radial distance from center to hole centers
|
||||
number_of_mounting_holes = 4; // Number of mounting holes
|
||||
mounting_hole_bore = 8; // Diameter of mounting holes counterbore
|
||||
round_radius = 4;
|
||||
motor_shaft_diameter = 5;
|
||||
hub_height = 10;
|
||||
|
||||
motor_width = 20; // Diameter of the motor shaft
|
||||
$fn = 128; // Number of fragments
|
||||
|
||||
diameter = 100;
|
||||
blade_width = 5;
|
||||
blade_length = diameter;
|
||||
|
||||
// Blade Height
|
||||
blade_height = 10;
|
||||
|
||||
// Number of ports/blades
|
||||
number_of_ports = 5;
|
||||
|
||||
// Pressure zone percentage
|
||||
pressure_zone = 0.4; // 30% of diameter
|
||||
|
||||
module impeller_blades() {
|
||||
difference() {
|
||||
intersection() {
|
||||
// Column with full diameter
|
||||
cylinder(d = diameter, h = blade_height);
|
||||
|
||||
// Blades
|
||||
union() {
|
||||
//rotate([180, 180, 180])
|
||||
for (i = [0:360/number_of_ports:360]) {
|
||||
rotate([0, 0, i - 90])
|
||||
translate([0, diameter / 2, 0])
|
||||
rotate([0, 0, 10])
|
||||
translate([-blade_width, -diameter / 2, 0])
|
||||
linear_extrude(height = blade_height, twist = 0, slices=360)
|
||||
square([blade_width, blade_length]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Subtract core cylinder
|
||||
translate([0, 0, -1])
|
||||
cylinder(d = diameter * pressure_zone, h = blade_height + 2);
|
||||
}
|
||||
}
|
||||
|
||||
module ports() {
|
||||
radius = diameter / 2;
|
||||
angle = 360 / number_of_ports / 2;
|
||||
|
||||
rotate([0, 0, 180 / number_of_ports])
|
||||
for (i = [0:360/number_of_ports:360]) {
|
||||
rotate([0, 0, i])
|
||||
translate([0, 0, (height - port_height) / 2])
|
||||
linear_extrude(height = port_height)
|
||||
polygon(concat(
|
||||
[[0, 0]], // Center point
|
||||
[for (j = [0 : $fn])
|
||||
[radius * cos(angle * j / $fn),
|
||||
radius * sin(angle * j / $fn)]]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Optional: If you want to render the blades independently for testing
|
||||
impeller_blades();
|
||||
ports();
|
||||
166
designs/openscad/tests/box.scad
Normal file
166
designs/openscad/tests/box.scad
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
include <Round-Anything-master/polyround.scad>
|
||||
|
||||
$fn = $preview ? 32 : 64;
|
||||
|
||||
module rounded_rectangle(width, length, height, radius1, radius2) {
|
||||
polyRoundExtrude([
|
||||
[-width/2, -length/2, 1], // Bottom-left point
|
||||
[-width/2, length/2, 1], // Top-left point
|
||||
[width/2, length/2, 1], // Top-right point
|
||||
[width/2, -length/2, 1] // Bottom-right point
|
||||
], height, radius1, radius2);
|
||||
}
|
||||
|
||||
module box() {
|
||||
difference() {
|
||||
// Outer rounded box
|
||||
minkowski() {
|
||||
cube([width + wall_thickness*2 + inset_distance*2,
|
||||
depth + wall_thickness*2 + inset_distance*2,
|
||||
height + wall_thickness*2 + inset_distance*2], center = true);
|
||||
sphere(r = radius);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Inner space (centered)
|
||||
minkowski() {
|
||||
cube([width + inset_distance*2,
|
||||
depth + inset_distance*2,
|
||||
height + inset_distance*2], center = true);
|
||||
sphere(r = radius);
|
||||
}
|
||||
|
||||
|
||||
/*cube([width,
|
||||
depth,
|
||||
height], center = true);
|
||||
*/
|
||||
|
||||
|
||||
translate([0, 0, height/2 + radius + wall_thickness + inset_distance])
|
||||
inset(width = width, height = depth);
|
||||
|
||||
rotate([180, 0, 0])
|
||||
translate([0, 0, height/2 + radius + wall_thickness + inset_distance])
|
||||
inset(width = width, height = depth);
|
||||
|
||||
rotate([90, 0, 0])
|
||||
translate([0, 0, depth/2 + radius + wall_thickness + inset_distance])
|
||||
inset(width = width, height = height);
|
||||
|
||||
rotate([-90, 0, 0])
|
||||
translate([0, 0, depth/2 + radius + wall_thickness + inset_distance])
|
||||
inset(width = width, height = height );
|
||||
|
||||
rotate([0, 90, 0])
|
||||
translate([0, 0, width/2 + radius + wall_thickness + inset_distance])
|
||||
inset(width = height, height = depth);
|
||||
|
||||
rotate([0, -90, 0])
|
||||
translate([0, 0, width/2 + radius + wall_thickness + inset_distance])
|
||||
inset(width = height, height = depth);
|
||||
|
||||
translate([0, 0, (height + inset_distance*2) / 2 + 1])
|
||||
minkowski() {
|
||||
cube([width + wall_thickness*2 + inset_distance*2 + 10, 4, 1], center = true);
|
||||
sphere(r = 0.5);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module inset(width, height) {
|
||||
translate([0, 0, -inset_inner_thickness - inset_outer_thickness])
|
||||
linear_extrude(height = inset_inner_thickness) {
|
||||
minkowski() {
|
||||
square([
|
||||
width - (2 * hole_inset_distance),
|
||||
height - (2 * hole_inset_distance)
|
||||
], center = true);
|
||||
circle(r = radius);
|
||||
}
|
||||
}
|
||||
|
||||
translate([0, 0, -inset_outer_thickness])
|
||||
linear_extrude(height = inset_outer_thickness) {
|
||||
minkowski() {
|
||||
square([width, height], center = true);
|
||||
circle(r = radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module rounded_inset(width, height) {
|
||||
/*(translate([0, 0, -inset_inner_thickness - inset_outer_thickness])
|
||||
linear_extrude(height = inset_inner_thickness) {
|
||||
minkowski() {
|
||||
square([
|
||||
width - (2 * hole_inset_distance),
|
||||
height - (2 * hole_inset_distance)
|
||||
], center = true);
|
||||
circle(r = radius);
|
||||
}
|
||||
}*/
|
||||
|
||||
translate([0, 0, -inset_inner_thickness*2 - inset_outer_thickness])
|
||||
rounded_rectangle(
|
||||
width - (2 * hole_inset_distance),
|
||||
height - (2 * hole_inset_distance),
|
||||
inset_inner_thickness*2, -2, -2);
|
||||
|
||||
translate([0, 0, -inset_outer_thickness])
|
||||
linear_extrude(height = inset_outer_thickness) {
|
||||
minkowski() {
|
||||
square([width, height], center = true);
|
||||
circle(r = radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module snap(){
|
||||
|
||||
}
|
||||
|
||||
module half_box() {
|
||||
difference(){
|
||||
intersection(){
|
||||
box();
|
||||
translate([0, 0, (height + wall_thickness*2 + radius*2 + inset_distance*2) / 2])
|
||||
cube([width + wall_thickness*2 + radius*2 + inset_distance*2,
|
||||
depth + wall_thickness*2 + radius*2 + inset_distance*2,
|
||||
height + wall_thickness*2 + radius*2 + inset_distance*2], center = true);
|
||||
}
|
||||
|
||||
/*
|
||||
minkowski() {
|
||||
cube([width+ inset_inner_thickness*2,
|
||||
depth+ inset_inner_thickness*2,
|
||||
3], center = true);
|
||||
sphere(r = radius);
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Example usage
|
||||
|
||||
width = 110; // Total width
|
||||
height = 40; // Total height
|
||||
depth = 110; // Total depth
|
||||
radius = 3; // Box corner radius
|
||||
wall_thickness = 3; // Wall thickness
|
||||
inset_distance = 4; //distance from inset side
|
||||
hole_inset_distance = 2; //distance from inset side
|
||||
hole_radius = 3; // Hole corner radius
|
||||
inset_inner_thickness = 1.7;
|
||||
inset_outer_thickness = 1.5;
|
||||
//
|
||||
half_box();
|
||||
//rounded_inset(width, height);
|
||||
|
||||
|
||||
|
||||
|
||||
153
designs/openscad/tests/impeller.scad
Normal file
153
designs/openscad/tests/impeller.scad
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
// Impeller Parameters
|
||||
diameter = 100; // Overall base diameter
|
||||
hub_diameter = 30; // Diameter of the central hub
|
||||
hub_height = 10; // Height of the central hub
|
||||
base_thickness = 3; // Thickness of the base
|
||||
shaft_diameter = 10; // Diameter of the central shaft hole
|
||||
blade_count = 6; // Number of blades
|
||||
blade_length = 35; // Length of blades from hub
|
||||
blade_thickness = 3; // Thickness of blades
|
||||
blade_height_at_hub = 5; // Blade height at the hub
|
||||
blade_height_at_diameter = 20; // Blade height at full diameter
|
||||
|
||||
module impeller_base() {
|
||||
// Base with hub
|
||||
difference() {
|
||||
union() {
|
||||
// Solid base disk
|
||||
cylinder(h = base_thickness, d = diameter, center = false);
|
||||
|
||||
// Central hub
|
||||
translate([0, 0, base_thickness])
|
||||
cylinder(h = hub_height, d = hub_diameter, center = false);
|
||||
}
|
||||
|
||||
// Countersink for shaft entry
|
||||
translate([0, 0, -0.1])
|
||||
cylinder(h = 1, d1 = shaft_diameter, d2 = shaft_diameter + 2, center = false);
|
||||
|
||||
// Through hole for shaft
|
||||
translate([0, 0, base_thickness - 0.1])
|
||||
cylinder(h = hub_height + 0.2, d = shaft_diameter, center = false);
|
||||
}
|
||||
}
|
||||
|
||||
module impeller_blade() {
|
||||
// Blade with base always touching the impeller base
|
||||
polyhedron(
|
||||
points = [
|
||||
// Bottom points at hub
|
||||
[hub_diameter/2, -blade_thickness/2, base_thickness],
|
||||
[hub_diameter/2, blade_thickness/2, base_thickness],
|
||||
|
||||
// Bottom points at full diameter
|
||||
[diameter/2, -blade_thickness/2, base_thickness],
|
||||
[diameter/2, blade_thickness/2, base_thickness],
|
||||
|
||||
// Top points at hub
|
||||
[hub_diameter/2, -blade_thickness/2, base_thickness + blade_height_at_hub],
|
||||
[hub_diameter/2, blade_thickness/2, base_thickness + blade_height_at_hub],
|
||||
|
||||
// Top points at full diameter
|
||||
[diameter/2, -blade_thickness/2, base_thickness + blade_height_at_diameter],
|
||||
[diameter/2, blade_thickness/2, base_thickness + blade_height_at_diameter]
|
||||
],
|
||||
faces = [
|
||||
[0, 2, 3, 1], // Bottom face
|
||||
[0, 1, 5, 4], // Inner side bottom
|
||||
[2, 6, 7, 3], // Outer side bottom
|
||||
[4, 5, 7, 6], // Top face
|
||||
[0, 4, 6, 2], // Side face 1
|
||||
[1, 3, 7, 5] // Side face 2
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
module impeller_blade() {
|
||||
polyhedron(
|
||||
points = [
|
||||
// Bottom points at hub - SHIFTED OUTWARD
|
||||
[hub_diameter/2 + base_thickness * tan(30), -blade_thickness/2, base_thickness],
|
||||
[hub_diameter/2 + base_thickness * tan(30), blade_thickness/2, base_thickness],
|
||||
|
||||
// Bottom points at full diameter - SHIFTED OUTWARD
|
||||
[diameter/2 + base_thickness * tan(30), -blade_thickness/2, base_thickness],
|
||||
[diameter/2 + base_thickness * tan(30), blade_thickness/2, base_thickness],
|
||||
|
||||
// Top points at hub - INCREASED RADIAL ANGLE
|
||||
[hub_diameter/2 + blade_height_at_hub * tan(30), -blade_thickness/2, base_thickness + blade_height_at_hub],
|
||||
[hub_diameter/2 + blade_height_at_hub * tan(30), blade_thickness/2, base_thickness + blade_height_at_hub],
|
||||
|
||||
// Top points at full diameter - INCREASED RADIAL ANGLE
|
||||
[diameter/2 + blade_height_at_diameter * tan(30), -blade_thickness/2, base_thickness + blade_height_at_diameter],
|
||||
[diameter/2 + blade_height_at_diameter * tan(30), blade_thickness/2, base_thickness + blade_height_at_diameter]
|
||||
],
|
||||
faces = [
|
||||
[0, 2, 3, 1], // Bottom face
|
||||
[0, 1, 5, 4], // Inner side bottom
|
||||
[2, 6, 7, 3], // Outer side bottom
|
||||
[4, 5, 7, 6], // Top face
|
||||
[0, 4, 6, 2], // Side face 1
|
||||
[1, 3, 7, 5] // Side face 2
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
module impeller_blades() {
|
||||
// Add blades
|
||||
for (i = [0:360/blade_count:359]) {
|
||||
rotate([0, 0, i]) {
|
||||
impeller_blade();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module impeller_column() {
|
||||
// Column Parameters
|
||||
column_height = blade_height_at_diameter + base_thickness; // Height matches blade height
|
||||
num_ports = blade_count; // Number of ports matching blade count
|
||||
wall_thickness = 5; // Thickness of the column wall
|
||||
outer_diameter = diameter; // Diameter of the impeller base
|
||||
|
||||
// Calculate port width based on equal arc length
|
||||
port_width = (PI * outer_diameter) / (num_ports * 2);
|
||||
|
||||
// Calculate inner diameter based on wall thickness
|
||||
inner_diameter = outer_diameter - (2 * wall_thickness);
|
||||
|
||||
// Rotate by half the port width
|
||||
rotate([0, 0, 180/(num_ports * 2)])
|
||||
difference() {
|
||||
union() {
|
||||
// Main cylindrical body
|
||||
cylinder(h = column_height, d = outer_diameter, $fn = 100);
|
||||
|
||||
// Close the bottom with wall_thickness height
|
||||
cylinder(h = wall_thickness, d = outer_diameter, $fn = 100);
|
||||
}
|
||||
|
||||
// Hollow out the center (open on top end)
|
||||
translate([0, 0, wall_thickness])
|
||||
cylinder(h = column_height + 1, d = inner_diameter, $fn = 100);
|
||||
|
||||
// Create evenly spaced rectangular ports
|
||||
for (i = [0 : num_ports - 1]) {
|
||||
rotate([0, 0, i * (360 / num_ports)])
|
||||
translate([outer_diameter/2 - wall_thickness * 2, -port_width/2, 0])
|
||||
cube([wall_thickness * 2 + 1, port_width, column_height + 10]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
module complete_impeller() {
|
||||
// Combine base and blades
|
||||
impeller_base();
|
||||
impeller_blades();
|
||||
//impeller_column();
|
||||
}
|
||||
|
||||
// Render the complete impeller
|
||||
complete_impeller();
|
||||
39
designs/openscad/tests/minkowski_play.scad
Normal file
39
designs/openscad/tests/minkowski_play.scad
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
module torus(major_radius, minor_radius, $fn=10) {
|
||||
rotate_extrude(convexity = 10)
|
||||
translate([major_radius, 0, 0])
|
||||
circle(r = minor_radius);
|
||||
}
|
||||
|
||||
module rounded_cube() {
|
||||
minkowski() {
|
||||
|
||||
|
||||
union(){
|
||||
sphere(r=2, $fn=10);
|
||||
/*
|
||||
difference(){
|
||||
rotate([180, 0, 0])
|
||||
cylinder(d = 10, h = 3, $fn=10);
|
||||
torus(major_radius = 5, minor_radius = 3);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
cube([10, 10, 10]); // Base shape
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
rounded_cube();
|
||||
// Example usage
|
||||
/*
|
||||
union(){
|
||||
sphere(r=2, $fn=100);
|
||||
difference(){
|
||||
rotate([180, 0, 0])
|
||||
cylinder(d = 10, h = 3, $fn=100);
|
||||
torus(major_radius = 5, minor_radius = 3);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
31
designs/openscad/tests/polyroundtest.scad
Normal file
31
designs/openscad/tests/polyroundtest.scad
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
include <Round-Anything-master/polyround.scad>
|
||||
|
||||
module rounded_rectangle(width, length, height, radius1, radius2) {
|
||||
polyRoundExtrude([
|
||||
[-width/2, -length/2, 1], // Bottom-left point
|
||||
[-width/2, length/2, 1], // Top-left point
|
||||
[width/2, length/2, 1], // Top-right point
|
||||
[width/2, -length/2, 1] // Bottom-right point
|
||||
], height, radius1, radius2);
|
||||
}
|
||||
|
||||
/*
|
||||
radiiPoints=[
|
||||
[0, 0, 0.5],
|
||||
[0, 5, 0.5],
|
||||
[5, 0, 0.5],
|
||||
[2, 0, 0.5],
|
||||
[2, -3, 1],
|
||||
[4, -3, 0],
|
||||
[4, -5, 0],
|
||||
[-2, -5, 0],
|
||||
[-2, -3, 0],
|
||||
[0, -3, 1]];
|
||||
extrudeWithRadius(3,1,1,50)
|
||||
polygon(polyRound(radiiPoints,30));
|
||||
*/
|
||||
|
||||
// Usage examples
|
||||
rounded_rectangle(10, 10, 4, -2, -2);
|
||||
|
||||
//extrudeWithRadius(5,-2,-2,50)square([5, 2]);
|
||||
553
designs/openscad/tests/siren_simple.scad
Normal file
553
designs/openscad/tests/siren_simple.scad
Normal file
|
|
@ -0,0 +1,553 @@
|
|||
/* [Siren General Parameters] */
|
||||
siren_diameter = 125; // Base diameter
|
||||
siren_height = 50; // Height of the rotor
|
||||
number_of_ports = 5; // Number of ports
|
||||
tolerance = 2; // Tolerance between stator and rotor
|
||||
pressure_zone = 0.4; // Percentage of diameter
|
||||
round_radius = 4; // Rounding of top/bottom
|
||||
$fn = $preview ? 32 : 128; // Number of fragments
|
||||
|
||||
/* [Stator Parameters] */
|
||||
stator_wall_thickness = 8; // Stator wall thickness
|
||||
number_of_mounting_holes = 4; // Number of mounting holes
|
||||
screw_diameter = 3.2; // Diameter of screws - M3 clearance hole (3.2mm diameter)
|
||||
screw_bore_diameter = 6.5;
|
||||
screw_insert_diameter = 4;
|
||||
screw_bore_depth = 3;
|
||||
screw_length = 10 - (stator_wall_thickness - screw_bore_depth);
|
||||
stator_screw_offset = siren_diameter/2 - stator_wall_thickness/2; //distance to screws
|
||||
|
||||
/* [Rotor Parameters] */
|
||||
rotor_wall_thickness = 3; // Rotor wall thickness
|
||||
hub_height = 5;
|
||||
hub_diameter = 12;
|
||||
blade_angle = 15;
|
||||
port_height = siren_height - (rotor_wall_thickness * 2) - (tolerance * 2); // Height of the port
|
||||
|
||||
/* [Motor Parameters] */
|
||||
motor_shaft_diameter = 8; // Diameter of the motor body
|
||||
motor_frame_diameter = 60;
|
||||
motor_diameter = 28; // Diameter of the motor body
|
||||
motor_height = 45;
|
||||
magnet_diameter = 7;
|
||||
motor_screw_x_offset = 9.5;
|
||||
motor_screw_y_offset = 8;
|
||||
motor_screw_offset = 8;
|
||||
motor_frame_screw_offset = motor_frame_diameter/2 - stator_wall_thickness/2; //distance to screws
|
||||
|
||||
// helper functions
|
||||
module ports(diameter, height, ports) {
|
||||
radius = diameter / 2;
|
||||
angle = 180 / ports;
|
||||
|
||||
rotate([0, 0, 180 / ports])
|
||||
for (i = [0:360/ports:360]) {
|
||||
rotate([0, 0, i])
|
||||
linear_extrude(height)
|
||||
polygon(concat(
|
||||
[[0, 0]], // Center point
|
||||
[for (j = [0 : $fn])
|
||||
[radius * cos(angle * j / $fn),
|
||||
radius * sin(angle * j / $fn)]]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
module column(outer_diameter, inner_diameter, height) {
|
||||
difference(){
|
||||
// Outer cylinder
|
||||
cylinder(d = outer_diameter, h = height);
|
||||
// Subtract inner cylinder
|
||||
cylinder(d = inner_diameter, h = height);
|
||||
}
|
||||
}
|
||||
|
||||
// Linear Interpolation
|
||||
function lerp(a, b, t) = a * (1 - t) + b * t;
|
||||
|
||||
// Arc between points function
|
||||
function arc_between_points(p1, p2, height, steps=20) =
|
||||
[for(t = [0:1/steps:1])
|
||||
let(
|
||||
x = lerp(p1.x, p2.x, t),
|
||||
y = lerp(p1.y, p2.y, t),
|
||||
// Parabolic arc height calculation
|
||||
arc_height = height * (1 - pow(2*t-1, 2))
|
||||
)
|
||||
[x, y + arc_height]
|
||||
];
|
||||
|
||||
function bezier_curve(t, p0, p1, p2, p3, p4) =
|
||||
pow(1-t, 4) * p0 +
|
||||
4 * pow(1-t, 3) * t * p1 +
|
||||
6 * pow(1-t, 2) * pow(t, 2) * p2 +
|
||||
4 * (1-t) * pow(t, 3) * p3 +
|
||||
pow(t, 4) * p4;
|
||||
|
||||
function curved_polygon(points, steps, x_shift, y_shift) =
|
||||
let(
|
||||
left_curve = [for (t = [0 : 1/steps : 1])
|
||||
bezier_curve(t, points[0], points[1], points[2], points[3], points[4])
|
||||
],
|
||||
right_curve = [for (pt = left_curve)
|
||||
[pt[0] + x_shift, pt[1] + y_shift]
|
||||
]
|
||||
)
|
||||
concat(left_curve, [for (i = [len(right_curve)-1 : -1 : 0]) right_curve[i]]);
|
||||
|
||||
module stator(diameter, height, p_height, ports) {
|
||||
difference() {
|
||||
|
||||
// Create column
|
||||
column(diameter, diameter - (stator_wall_thickness * 2), height);
|
||||
|
||||
// Subtract ports
|
||||
translate([0, 0, (height - p_height) / 2])
|
||||
ports(diameter, p_height, number_of_ports);
|
||||
|
||||
// 4 M3 mounting holes
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
// Top
|
||||
rotate([0, 0, i])
|
||||
translate([stator_screw_offset, 0, -1])
|
||||
cylinder(d = screw_insert_diameter, h = screw_length + 1);
|
||||
|
||||
// Bottom
|
||||
rotate([0, 0, i])
|
||||
translate([stator_screw_offset, 0, height - screw_length]) // Adjust position as needed
|
||||
cylinder(d = screw_insert_diameter, h = screw_length + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module impeller_blades_straight(rotor_inner_diameter, rotor_height) {
|
||||
difference() {
|
||||
intersection() {
|
||||
// Column with full diameter
|
||||
cylinder(d = rotor_inner_diameter, h = rotor_height);
|
||||
|
||||
// Blades
|
||||
union() {
|
||||
for (i = [0:360/number_of_ports:360]) {
|
||||
rotate([0, 0, i - 90])
|
||||
translate([0, rotor_inner_diameter / 2, 0])
|
||||
rotate([0, 0, blade_angle])
|
||||
|
||||
translate([-rotor_wall_thickness, -(rotor_inner_diameter / 2), 0])
|
||||
linear_extrude(height = rotor_height, twist = 0, slices=360)
|
||||
square([rotor_wall_thickness, diameter]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Subtract core cylinder
|
||||
translate([0, 0, -1])
|
||||
cylinder(d = diameter * pressure_zone, h = height + 10);
|
||||
}
|
||||
}
|
||||
|
||||
module impeller_blade(radius, rotor_height){
|
||||
// Generate points
|
||||
points1 = arc_between_points([0, -hub_diameter / 2], [radius, 0], -blade_angle);
|
||||
points2 = [for(p = [len(points1)-1:-1:0]) points1[p] - [0, -rotor_wall_thickness]];
|
||||
|
||||
// Create the blade by combining top and bottom points
|
||||
linear_extrude(height = rotor_height)
|
||||
polygon(concat(
|
||||
points1,
|
||||
points2
|
||||
));
|
||||
}
|
||||
|
||||
module impeller_blades_curved(radius, rotor_height){
|
||||
difference() {
|
||||
|
||||
union() {
|
||||
// Rotate and create blades
|
||||
for (i = [0:360/number_of_ports:360]) {
|
||||
rotate([0, 0, i])
|
||||
impeller_blade(radius, rotor_height);
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract core cylinder
|
||||
/*
|
||||
rounding_radius = 25;
|
||||
translate([0, 0, rotor_wall_thickness + rounding_radius])
|
||||
minkowski() {
|
||||
// Original cylinder, reduced by twice the rounding radius
|
||||
cylinder(
|
||||
d = diameter * pressure_zone - (2 * rounding_radius),
|
||||
h = 1000
|
||||
);
|
||||
|
||||
// Sphere for rounding
|
||||
sphere(r = rounding_radius);
|
||||
}
|
||||
*/
|
||||
|
||||
top_rounding_radius = 5;
|
||||
bottom_rounding_radius = rotor_height / 2 - 1;
|
||||
translate([0, 0, rotor_height - top_rounding_radius - 0])
|
||||
union(){
|
||||
difference() {
|
||||
// Column with diameter = 2 * top_rounding_radius
|
||||
cylinder(d = (diameter * pressure_zone) + (top_rounding_radius * 2), h = top_rounding_radius + 10);
|
||||
|
||||
// Top torus
|
||||
rotate_extrude()
|
||||
translate([((diameter * pressure_zone) + (top_rounding_radius * 2)) / 2, 0, 0])
|
||||
circle(r = top_rounding_radius);
|
||||
}
|
||||
|
||||
rotate([180, 0, 0])
|
||||
minkowski() {
|
||||
// Original cylinder, reduced by twice the rounding radius
|
||||
cylinder(
|
||||
d = (diameter * pressure_zone) - (2 * bottom_rounding_radius),
|
||||
h = rotor_height - bottom_rounding_radius - (top_rounding_radius * 2) + 2
|
||||
);
|
||||
|
||||
// Sphere for rounding
|
||||
sphere(r = bottom_rounding_radius);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module rotor() {
|
||||
rotor_diameter = diameter - (stator_wall_thickness * 2) - (tolerance * 2);
|
||||
rotor_height = height - (tolerance * 2);
|
||||
rotor_inner_diameter = rotor_diameter - (rotor_wall_thickness * 2);
|
||||
|
||||
difference() {
|
||||
union(){
|
||||
difference() {
|
||||
// Outer cylinder
|
||||
cylinder(d = rotor_diameter, h = rotor_height);
|
||||
|
||||
// Subtract inner cylinder
|
||||
translate([0, 0, rotor_wall_thickness])
|
||||
cylinder(d = rotor_inner_diameter, h = rotor_height + 2);
|
||||
|
||||
// Subtract ports
|
||||
translate([0, 0, -tolerance])
|
||||
ports();
|
||||
}
|
||||
|
||||
/*
|
||||
// Central column for hub added after subtraction
|
||||
translate([0, 0, rotor_wall_thickness])
|
||||
cylinder(d = hub_diameter, h = hub_height);
|
||||
*/
|
||||
|
||||
// Add blades
|
||||
impeller_blades_curved(rotor_inner_diameter / 2, rotor_height);
|
||||
}
|
||||
|
||||
translate([0, 0, -10])
|
||||
cylinder(d = hub_diameter, h = 100);
|
||||
|
||||
translate([0, 0, 0])
|
||||
hub_attachment(0.2);
|
||||
|
||||
/*
|
||||
// Subtract motor shaft hole
|
||||
translate([0, 0, -10])
|
||||
intersection(){
|
||||
cylinder(d = motor_shaft_diameter, h = 100);
|
||||
cube([motor_shaft_diameter, motor_shaft_diameter-1, 100], center = true);
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module hub() {
|
||||
// Subtract motor shaft hole
|
||||
difference(){
|
||||
cylinder(d = hub_diameter, h = hub_height + 3);
|
||||
translate([0, 0, -1])
|
||||
intersection(){
|
||||
cylinder(d = motor_shaft_diameter, h = 100);
|
||||
cube([motor_shaft_diameter, motor_shaft_diameter-1, 100], center = true);
|
||||
};
|
||||
cylinder(d = motor_shaft_diameter, h = 2);
|
||||
}
|
||||
hub_attachment();
|
||||
}
|
||||
|
||||
module hub_attachment(aug = 0) {
|
||||
outer_diameter = hub_diameter + (tolerance * 6) + (aug * 2);
|
||||
mid_diameter = hub_diameter + (tolerance * 2) + (aug * 2);
|
||||
inner_diameter = outer_diameter - (tolerance * 2) - (aug * 2);
|
||||
difference(){
|
||||
column(outer_diameter, inner_diameter, rotor_wall_thickness);
|
||||
translate([0, 0, -10])
|
||||
ports();
|
||||
}
|
||||
translate([0, 0, rotor_wall_thickness])
|
||||
column(outer_diameter, mid_diameter, rotor_wall_thickness);
|
||||
column(mid_diameter, hub_diameter - (aug * 2), rotor_wall_thickness * 2);
|
||||
}
|
||||
|
||||
module stator_top() {
|
||||
difference() {
|
||||
// Rounded top
|
||||
intersection() {
|
||||
// Unrounded full cylinder
|
||||
cylinder(d = diameter, h = stator_wall_thickness);
|
||||
|
||||
// Add Minkowski rounded version
|
||||
minkowski() {
|
||||
cylinder(h = stator_wall_thickness - round_radius, d = diameter - round_radius * 2);
|
||||
sphere(r = round_radius);
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract central opening for pressure zone
|
||||
translate([0, 0, -1])
|
||||
cylinder(d = diameter * pressure_zone, h = stator_wall_thickness + 2);
|
||||
|
||||
// Subtract mounting holes for stator
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
// Subtract screw hole
|
||||
rotate([0, 0, i])
|
||||
translate([stator_screw_offset, 0, -1]) // Adjusted position
|
||||
cylinder(d = screw_diameter, h = stator_wall_thickness + 2);
|
||||
|
||||
// Subtract counterbore
|
||||
rotate([0, 0, i])
|
||||
translate([stator_screw_offset, 0, stator_wall_thickness - screw_bore_depth]) // Adjusted position
|
||||
cylinder(d = screw_bore_diameter, h = stator_wall_thickness);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module stator_bottom() {
|
||||
difference() {
|
||||
// Rounded bottom
|
||||
intersection() {
|
||||
// Unrounded full cylinder
|
||||
cylinder(d = diameter, h = stator_wall_thickness);
|
||||
|
||||
// Minkowski rounded version
|
||||
minkowski() {
|
||||
cylinder(h = stator_wall_thickness - round_radius, d = diameter - round_radius * 2);
|
||||
sphere(r = round_radius);
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract central hole for motor
|
||||
translate([0, 0, -1])
|
||||
cylinder(d = motor_diameter + (tolerance * 2), h = stator_wall_thickness + 5);
|
||||
|
||||
// Subtract mounting holes for stator
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
// Subtract screw hole
|
||||
rotate([0, 0, i])
|
||||
translate([stator_screw_offset, 0, -1]) // Adjusted position
|
||||
cylinder(d = screw_diameter, h = stator_wall_thickness + 2);
|
||||
|
||||
// Subtract counterbore
|
||||
rotate([0, 0, i])
|
||||
translate([stator_screw_offset, 0, stator_wall_thickness - screw_bore_depth]) // Adjusted position
|
||||
cylinder(d = screw_bore_diameter, h = stator_wall_thickness);
|
||||
}
|
||||
|
||||
// Subtract mounting holes for motor frame
|
||||
rotate([180, 0, 0])
|
||||
translate([0, 0, -stator_wall_thickness])
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
// Subtract screw hole
|
||||
rotate([0, 0, i])
|
||||
translate([motor_frame_screw_offset, 0, -1]) // Adjusted position
|
||||
cylinder(d = screw_diameter, h = stator_wall_thickness + 2);
|
||||
|
||||
// Subtract counterbore
|
||||
rotate([0, 0, i])
|
||||
translate([motor_frame_screw_offset, 0, stator_wall_thickness - screw_bore_depth]) // Adjusted position
|
||||
cylinder(d = screw_bore_diameter, h = stator_wall_thickness);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module motor_frame() {
|
||||
difference() {
|
||||
intersection(){
|
||||
union(){
|
||||
|
||||
// Rounded base
|
||||
rotate([180, 0, 0])
|
||||
translate([0, 0, -stator_wall_thickness])
|
||||
intersection() {
|
||||
// Unrounded full cylinder
|
||||
cylinder(d = motor_frame_diameter, h = stator_wall_thickness);
|
||||
|
||||
// Minkowski rounded version
|
||||
minkowski() {
|
||||
cylinder(h = stator_wall_thickness - round_radius, d = motor_frame_diameter - round_radius * 2);
|
||||
sphere(r = round_radius);
|
||||
}
|
||||
}
|
||||
|
||||
translate([0, 0, stator_wall_thickness])
|
||||
// Outer cylinder
|
||||
cylinder(d = motor_frame_diameter, h = motor_height);
|
||||
}
|
||||
|
||||
// Outer cylinder
|
||||
cylinder(d = motor_frame_diameter, h = motor_height);
|
||||
}
|
||||
|
||||
// Subtract inner cylinder
|
||||
translate([0, 0, stator_wall_thickness])
|
||||
cylinder(d = motor_frame_diameter - (stator_wall_thickness * 2), h = motor_height + 2);
|
||||
|
||||
// Subtract ports
|
||||
rotate([0, 0, -90/number_of_mounting_holes])
|
||||
ports(number_of_mounting_holes);
|
||||
|
||||
intersection(){
|
||||
difference() {
|
||||
// Inner cylinder (subtract)
|
||||
translate([0, 0, -1])
|
||||
cylinder(d = (motor_frame_screw_offset * 2) - stator_wall_thickness, h = motor_height + 2);
|
||||
|
||||
translate([0, 0, 0])
|
||||
cylinder(d = (motor_screw_y_offset * 2) + stator_wall_thickness, h = motor_height + 2);
|
||||
}
|
||||
|
||||
// Subtract ports
|
||||
translate([0, 0, -stator_wall_thickness - 2])
|
||||
rotate([0, 0, -90/number_of_mounting_holes])
|
||||
ports(number_of_mounting_holes);
|
||||
}
|
||||
|
||||
// Subtract mounting holes for frame
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
rotate([0, 0, i])
|
||||
translate([motor_frame_screw_offset, 0, motor_height - screw_length - 5]) // Adjusted position
|
||||
cylinder(d = screw_insert_diameter, h = 15);
|
||||
}
|
||||
|
||||
// Subtract magnet hole
|
||||
translate([0, 0, -10])
|
||||
cylinder(d = magnet_diameter + (tolerance * 2), h = 100);
|
||||
|
||||
// Subtract mounting holes for motor
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
rotate([0, 0, i]) {
|
||||
// Use modulo to alternate between x and y offsets
|
||||
motor_screw_offset = (floor(i / (360/number_of_mounting_holes)) % 2 == 0) ?
|
||||
motor_screw_x_offset : motor_screw_y_offset;
|
||||
|
||||
translate([motor_screw_offset, 0, -1])
|
||||
cylinder(d = screw_diameter, h = 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module base() {
|
||||
union(){
|
||||
difference(){
|
||||
rotate_extrude() {
|
||||
polygon(curved_polygon(
|
||||
points = [
|
||||
[diameter / 3, motor_height],
|
||||
[motor_frame_diameter / 2, motor_height - 5],
|
||||
[motor_frame_diameter / 2, 0],
|
||||
[motor_frame_diameter / 2, -10],
|
||||
[diameter / 3, -15]
|
||||
],
|
||||
steps = 100,
|
||||
x_shift = -stator_wall_thickness,
|
||||
y_shift = 0
|
||||
));
|
||||
}
|
||||
// Subtract ports
|
||||
translate([0, 0, -25])
|
||||
rotate([0, 0, -90/number_of_mounting_holes])
|
||||
ports(number_of_mounting_holes, 45);
|
||||
|
||||
/*
|
||||
translate([0, 0, -19])
|
||||
// Subtract mounting holes for stator
|
||||
for (i = [0:360/number_of_mounting_holes:360]) {
|
||||
// Subtract screw hole
|
||||
rotate([0, 0, i])
|
||||
translate([stator_screw_offset, 0, -1]) // Adjusted position
|
||||
cylinder(d = screw_diameter, h = stator_wall_thickness + 2);
|
||||
|
||||
// Subtract counterbore
|
||||
rotate([0, 0, i])
|
||||
translate([stator_screw_offset, 0, stator_wall_thickness - screw_bore_depth]) // Adjusted position
|
||||
cylinder(d = screw_bore_diameter, h = stator_wall_thickness);
|
||||
}
|
||||
*/
|
||||
}
|
||||
motor_frame();
|
||||
translate([0, 0, -23])
|
||||
stator_top();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
stator(siren_diameter, siren_height, port_height, number_of_ports);
|
||||
//translate([diameter - 10, 0, tolerance])
|
||||
/*
|
||||
intersection(){
|
||||
cylinder(h = 30, d = 30);
|
||||
rotor();
|
||||
}
|
||||
*/
|
||||
|
||||
rotor();
|
||||
/*
|
||||
translate([diameter - 10, 0, height + 10])
|
||||
hub_attachment();
|
||||
*/
|
||||
/*
|
||||
translate([0, 0, height + 2])
|
||||
stator_top();
|
||||
rotate([180, 0, 0])
|
||||
translate([0, 0, 2])
|
||||
stator_bottom();
|
||||
//translate([0, 0, -motor_height - 10])
|
||||
//motor_frame();
|
||||
translate([0, 0, -motor_height - 15])
|
||||
base();
|
||||
*/
|
||||
|
||||
/*
|
||||
stator();
|
||||
translate([diameter + 10, 0, tolerance])
|
||||
rotor();
|
||||
translate([diameter * 2 + 10, 0, height + 2])
|
||||
stator_top();
|
||||
rotate([180, 0, 0])
|
||||
translate([-diameter - 10, 0, 2])
|
||||
stator_bottom();
|
||||
translate([-diameter * 2 - 10, 0, -motor_height - 10])
|
||||
motor_frame();
|
||||
translate([-diameter * 3 - 10, 0, -motor_height - 10])
|
||||
base();
|
||||
*/
|
||||
|
||||
/*
|
||||
// for testing
|
||||
difference(){
|
||||
cylinder(h=5.5, d=20);
|
||||
translate([-5, 0, -1])
|
||||
cylinder(h=10, d=screw_diameter);
|
||||
translate([-5, 0, 3])
|
||||
cylinder(h=5, d=screw_bore_diameter);
|
||||
|
||||
translate([5, 0, -1])
|
||||
cylinder(h=10, d=screw_insert_diameter);
|
||||
}
|
||||
*/
|
||||
|
||||
75
designs/openscad/tests/siren_stator.scad
Normal file
75
designs/openscad/tests/siren_stator.scad
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
|
||||
// Air Raid Siren Stator Parameters
|
||||
outer_diameter = 200; // Outer diameter of the stator
|
||||
column_height = 100; // Height of the entire column
|
||||
num_ports = 7; // Number of rectangular ports (works with odd or even)
|
||||
port_height = 70; // Height of the ports
|
||||
wall_thickness = 5; // Thickness of the column wall
|
||||
|
||||
module air_raid_siren_stator() {
|
||||
// Calculate port width based on equal arc length
|
||||
port_width = (PI * outer_diameter) / (num_ports * 2);
|
||||
|
||||
// Calculate vertical position to center the ports
|
||||
port_vertical_offset = (column_height - port_height) / 2;
|
||||
|
||||
// Calculate inner diameter based on wall thickness
|
||||
inner_diameter = outer_diameter - (2 * wall_thickness);
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
// Main cylindrical body
|
||||
cylinder(h = column_height, d = outer_diameter, $fn = 100);
|
||||
|
||||
// Close the bottom with wall_thickness height
|
||||
cylinder(h = wall_thickness, d = outer_diameter, $fn = 100);
|
||||
}
|
||||
|
||||
// Hollow out the center (open on top end)
|
||||
translate([0, 0, wall_thickness])
|
||||
cylinder(h = column_height + 1, d = inner_diameter, $fn = 100);
|
||||
|
||||
// Create evenly spaced rectangular ports
|
||||
for (i = [0 : num_ports - 1]) {
|
||||
rotate([0, 0, i * (360 / num_ports)])
|
||||
translate([outer_diameter/2 - wall_thickness * 2, -port_width/2, port_vertical_offset])
|
||||
cube([wall_thickness * 2 + 1, port_width, port_height]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module impeller() {
|
||||
// Calculate port width based on equal arc length
|
||||
port_width = (PI * outer_diameter) / (num_ports * 2);
|
||||
|
||||
// Calculate vertical position to center the ports
|
||||
port_vertical_offset = (column_height - port_height) / 2;
|
||||
|
||||
// Calculate inner diameter based on wall thickness
|
||||
inner_diameter = outer_diameter - (2 * wall_thickness);
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
// Main cylindrical body
|
||||
cylinder(h = column_height, d = outer_diameter, $fn = 100);
|
||||
|
||||
// Close the bottom with wall_thickness height
|
||||
cylinder(h = wall_thickness, d = outer_diameter, $fn = 100);
|
||||
}
|
||||
|
||||
// Hollow out the center (open on top end)
|
||||
translate([0, 0, wall_thickness])
|
||||
cylinder(h = column_height + 1, d = inner_diameter, $fn = 100);
|
||||
|
||||
// Create evenly spaced rectangular ports
|
||||
for (i = [0 : num_ports - 1]) {
|
||||
rotate([0, 0, i * (360 / num_ports)])
|
||||
translate([outer_diameter/2 - wall_thickness * 2, -port_width/2, port_vertical_offset])
|
||||
cube([wall_thickness * 2 + 1, port_width, port_height]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render the stator
|
||||
//air_raid_siren_stator();
|
||||
impeller();
|
||||
313
designs/openscad/tests/siren_stator_custom.scad
Normal file
313
designs/openscad/tests/siren_stator_custom.scad
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
/* [Stator Parameters] */
|
||||
Outer_Diameter = 200; //[10:1:500] Outer diameter of the stator
|
||||
Column_Height = 100; //[10:1:500] Height of the entire column
|
||||
Base_Height = 5; //[1:0.1:50] Height of the base
|
||||
Number_of_Ports = 7; //[3:1:20] Number of rectangular ports (works with odd or even)
|
||||
Port_Height = 70; //[10:1:500] Height of the ports
|
||||
Wall_Thickness = 5; //[1:0.1:50] Thickness of the column wall
|
||||
Tolerance = 2; //[0:0.1:5]
|
||||
|
||||
/* [Hub Parameters] */
|
||||
Hub_Diameter = 10; //[10:1:500] Diameter of the rotor hub
|
||||
Shaft_Diameter = 5; //[1:0.1:50] Diameter of the motor shaft
|
||||
|
||||
/* [Blade Parameters] */
|
||||
Blade_Thickness = 5; //[1:0.1:20] Thickness of each blade
|
||||
|
||||
module stator_core(outer_diameter, number_of_ports, column_height, port_height, base_height, wall_thickness, port_vertical_offset = 0) {
|
||||
// Calculate port width based on equal arc length
|
||||
port_width = (PI * outer_diameter) / (number_of_ports * 2);
|
||||
|
||||
// Calculate default vertical position to center the ports if no offset is provided
|
||||
default_port_vertical_offset = (column_height - port_height) / 2;
|
||||
|
||||
// Use the provided offset or fall back to the centered position
|
||||
final_port_vertical_offset = port_vertical_offset != 0 ?
|
||||
default_port_vertical_offset - (port_vertical_offset / 2):
|
||||
default_port_vertical_offset;
|
||||
|
||||
// Calculate inner diameter based on wall thickness
|
||||
inner_diameter = outer_diameter - wall_thickness;
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
// Base cylinder
|
||||
cylinder(h = base_height, d = outer_diameter, $fn = 100);
|
||||
|
||||
// Main cylindrical body
|
||||
translate([0, 0, base_height])
|
||||
cylinder(h = column_height, d = outer_diameter, $fn = 100);
|
||||
}
|
||||
|
||||
// Hollow out the center (open on top end)
|
||||
translate([0, 0, base_height])
|
||||
cylinder(h = column_height + 1, d = inner_diameter, $fn = 100);
|
||||
|
||||
// Create evenly spaced rectangular ports
|
||||
for (i = [0 : number_of_ports - 1]) {
|
||||
rotate([0, 0, i * (360 / number_of_ports)])
|
||||
translate([outer_diameter/2 - wall_thickness * 2, -port_width/2, base_height + final_port_vertical_offset])
|
||||
cube([wall_thickness * 2 + 1, port_width, port_height], center = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module stator() {
|
||||
// Local variables set to global variables
|
||||
outer_diameter = Outer_Diameter;
|
||||
number_of_ports = Number_of_Ports;
|
||||
column_height = Column_Height;
|
||||
port_height = Port_Height;
|
||||
base_height = Base_Height;
|
||||
wall_thickness = Wall_Thickness;
|
||||
|
||||
// Use the module instead of calling it as a function
|
||||
stator_core(outer_diameter, number_of_ports, column_height, port_height, base_height, wall_thickness);
|
||||
}
|
||||
|
||||
module stator() {
|
||||
// Local variables set to global variables
|
||||
outer_diameter = Outer_Diameter;
|
||||
number_of_ports = Number_of_Ports;
|
||||
column_height = Column_Height;
|
||||
port_height = Port_Height;
|
||||
base_height = Base_Height;
|
||||
wall_thickness = Wall_Thickness;
|
||||
|
||||
// Extension dimensions
|
||||
extension_length = 20; // Length of extension
|
||||
extension_width = 15; // Width of extension
|
||||
fillet_radius = 2; // Fillet radius
|
||||
screw_hole_diameter = 4; // M4 screw hole
|
||||
support_ring_diameter = screw_hole_diameter + 6;
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
// Original stator core
|
||||
stator_core(outer_diameter, number_of_ports, column_height, port_height, base_height, wall_thickness);
|
||||
|
||||
// Top extensions at 4 cardinal points
|
||||
translate([0, 0, base_height + column_height - wall_thickness])
|
||||
for (i = [0 : 3]) {
|
||||
rotate([0, 0, i * 90])
|
||||
translate([outer_diameter/2, -extension_width/2, 0])
|
||||
hull() {
|
||||
// Corners with fillet
|
||||
// Bottom left
|
||||
translate([fillet_radius, fillet_radius, 0])
|
||||
cylinder(r = fillet_radius, h = wall_thickness, $fn = 50);
|
||||
|
||||
// Bottom right
|
||||
translate([extension_length - fillet_radius, fillet_radius, 0])
|
||||
cylinder(r = fillet_radius, h = wall_thickness, $fn = 50);
|
||||
|
||||
// Top left
|
||||
translate([fillet_radius, extension_width - fillet_radius, 0])
|
||||
cylinder(r = fillet_radius, h = wall_thickness, $fn = 50);
|
||||
|
||||
// Top right
|
||||
translate([extension_length - fillet_radius, extension_width - fillet_radius, 0])
|
||||
cylinder(r = fillet_radius, h = wall_thickness, $fn = 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// M4 Screw holes through top extensions
|
||||
translate([0, 0, base_height + column_height - wall_thickness])
|
||||
for (i = [0 : 3]) {
|
||||
rotate([0, 0, i * 90])
|
||||
translate([outer_diameter/2 + extension_length/2, 0, -1])
|
||||
cylinder(h = wall_thickness + 2, d = screw_hole_diameter, $fn = 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
module blade(outer_diameter, column_height, blade_thickness, blade_color) {
|
||||
// Bezier curve function (cubic)
|
||||
function bezier_cubic(p0, p1, p2, p3, t) =
|
||||
pow(1-t, 3) * p0 +
|
||||
3 * pow(1-t, 2) * t * p1 +
|
||||
3 * (1-t) * pow(t, 2) * p2 +
|
||||
pow(t, 3) * p3;
|
||||
|
||||
// Generate Bezier curve points
|
||||
function generate_bezier_points(p0, p1, p2, p3, steps=20) =
|
||||
[for (i = [0:steps]) bezier_cubic(p0, p1, p2, p3, i/steps)];
|
||||
|
||||
// Control points for the Bezier curve
|
||||
p0 = [0, 0]; // Start point
|
||||
p1 = [outer_diameter / 3, column_height];
|
||||
p2 = [outer_diameter / 8, column_height];
|
||||
p3 = [outer_diameter / 2, column_height]; // End point
|
||||
|
||||
// Generate curve points
|
||||
curve_points = generate_bezier_points(p0, p1, p2, p3);
|
||||
|
||||
// Combine points to create polygon
|
||||
points = concat(
|
||||
curve_points,
|
||||
[[outer_diameter/2, 0]] // Close the polygon
|
||||
);
|
||||
|
||||
color(blade_color)
|
||||
rotate([90, 0, 0]) // Rotate 90 degrees around X-axis
|
||||
linear_extrude(height = blade_thickness, center = true)
|
||||
polygon(points = points);
|
||||
}
|
||||
*/
|
||||
|
||||
module blade(outer_diameter, column_height, blade_thickness, blade_color) {
|
||||
// Rectangular blade points
|
||||
points = [
|
||||
[outer_diameter * 0.5 / 2, 0], // Start at 40% of radius
|
||||
[outer_diameter * 0.5 / 2, column_height],// Top left
|
||||
[outer_diameter / 2, column_height],// Top right
|
||||
[outer_diameter / 2, 0] // Bottom right
|
||||
];
|
||||
|
||||
color(blade_color)
|
||||
rotate([90, 0, 0]) // Rotate 90 degrees around X-axis
|
||||
linear_extrude(height = blade_thickness, center = true)
|
||||
polygon(points = points);
|
||||
}
|
||||
|
||||
|
||||
module rotor() {
|
||||
// Local variables set to global variables
|
||||
outer_diameter = Outer_Diameter - Wall_Thickness - Tolerance;
|
||||
number_of_ports = Number_of_Ports;
|
||||
column_height = Column_Height - Base_Height - (Tolerance * 2);
|
||||
port_height = Port_Height;
|
||||
base_height = Base_Height;
|
||||
wall_thickness = Wall_Thickness;
|
||||
port_vertical_offset = Tolerance * 2 + 1;
|
||||
|
||||
// Hub parameters from global variables
|
||||
hub_diameter = Hub_Diameter;
|
||||
shaft_diameter = Shaft_Diameter;
|
||||
hub_height = base_height * 2;
|
||||
|
||||
// Blade parameters from global variables
|
||||
number_of_blades = Number_of_Ports;
|
||||
blade_thickness = Blade_Thickness;
|
||||
|
||||
rotation_offset = (360 / (number_of_ports * 4)) +
|
||||
((blade_thickness / (PI * outer_diameter)) * 360 / 2);
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
// Original stator_core module call
|
||||
stator_core(outer_diameter, number_of_ports, column_height, port_height, base_height, wall_thickness, port_vertical_offset);
|
||||
|
||||
// Hub for motor attachment
|
||||
translate([0, 0, 0])
|
||||
cylinder(h = hub_height, d = hub_diameter, $fn = 100);
|
||||
|
||||
// Add blades
|
||||
for (i = [0 : number_of_blades - 1]) {
|
||||
rotate([0, 0, i * (360 / number_of_ports) + rotation_offset])
|
||||
//rotate([0, 0, i * (360 / number_of_ports) + rotation_offset - 10])
|
||||
mirror([0, 1, 0]) // Mirror along Z-axis
|
||||
translate([0, 0, base_height])
|
||||
//translate([40, 0, base_height])
|
||||
//rotate([0, 0, -20])
|
||||
//translate([-40, 0, 0])
|
||||
blade(
|
||||
outer_diameter - wall_thickness,
|
||||
column_height,
|
||||
blade_thickness,
|
||||
"red" // Blade color
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Shaft hole for motor attachment
|
||||
translate([0, 0, -1])
|
||||
cylinder(h = hub_height + 2, d = shaft_diameter, $fn = 100);
|
||||
}
|
||||
}
|
||||
|
||||
module stator_cap() {
|
||||
outer_diameter = Outer_Diameter;
|
||||
wall_thickness = Wall_Thickness;
|
||||
column_height = Column_Height;
|
||||
base_height = Base_Height;
|
||||
|
||||
// Central hole diameter (60% of total diameter)
|
||||
central_hole_diameter = outer_diameter * 0.6;
|
||||
|
||||
// M4 screw specifications
|
||||
screw_hole_diameter = 4;
|
||||
|
||||
// Extension dimensions
|
||||
extension_length = 20; // Length of extension
|
||||
extension_width = 15; // Width of extension
|
||||
fillet_radius = 2; // Fillet radius
|
||||
|
||||
// Screw hole support ring
|
||||
support_ring_diameter = screw_hole_diameter + 6; // Adds extra material around screw hole
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
// Base solid cylinder
|
||||
cylinder(h = wall_thickness, d = outer_diameter, $fn = 100);
|
||||
|
||||
// Screw hole extensions at 4 cardinal points
|
||||
for (i = [0 : 3]) {
|
||||
rotate([0, 0, i * 90])
|
||||
translate([outer_diameter/2 - wall_thickness, -extension_width/2, 0]) {
|
||||
difference() {
|
||||
// Filleted extension
|
||||
hull() {
|
||||
// Corners with fillet
|
||||
// Bottom left
|
||||
translate([fillet_radius, fillet_radius, 0])
|
||||
cylinder(r = fillet_radius, h = wall_thickness, $fn = 50);
|
||||
|
||||
// Bottom right
|
||||
translate([extension_length - fillet_radius, fillet_radius, 0])
|
||||
cylinder(r = fillet_radius, h = wall_thickness, $fn = 50);
|
||||
|
||||
// Top left
|
||||
translate([fillet_radius, extension_width - fillet_radius, 0])
|
||||
cylinder(r = fillet_radius, h = wall_thickness, $fn = 50);
|
||||
|
||||
// Top right
|
||||
translate([extension_length - fillet_radius, extension_width - fillet_radius, 0])
|
||||
cylinder(r = fillet_radius, h = wall_thickness, $fn = 50);
|
||||
}
|
||||
|
||||
// Centered screw hole support ring
|
||||
translate([extension_length/2, extension_width/2, -1])
|
||||
cylinder(h = wall_thickness + 2, d = support_ring_diameter, $fn = 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Central hole
|
||||
translate([0, 0, -1])
|
||||
cylinder(h = wall_thickness + 2, d = central_hole_diameter, $fn = 100);
|
||||
|
||||
// M4 Screw holes through extensions
|
||||
for (i = [0 : 3]) {
|
||||
rotate([0, 0, i * 90])
|
||||
translate([outer_diameter/2 + extension_length/2 - wall_thickness, 0, -1])
|
||||
cylinder(h = wall_thickness + 2, d = screw_hole_diameter, $fn = 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Render components separately
|
||||
stator();
|
||||
/*
|
||||
translate([Outer_Diameter, 0, Base_Height + Tolerance])
|
||||
rotor();
|
||||
|
||||
// Render cap separately and offset
|
||||
translate([0, 0, Base_Height + Column_Height + Tolerance])
|
||||
stator_cap();
|
||||
*/
|
||||
BIN
designs/stl/back_face.stl
Normal file
BIN
designs/stl/back_face.stl
Normal file
Binary file not shown.
BIN
designs/stl/base.stl
Normal file
BIN
designs/stl/base.stl
Normal file
Binary file not shown.
BIN
designs/stl/base_125.stl
Normal file
BIN
designs/stl/base_125.stl
Normal file
Binary file not shown.
BIN
designs/stl/bottom_face.stl
Normal file
BIN
designs/stl/bottom_face.stl
Normal file
Binary file not shown.
BIN
designs/stl/bottom_face_test.stl
Normal file
BIN
designs/stl/bottom_face_test.stl
Normal file
Binary file not shown.
BIN
designs/stl/box_snap.stl
Normal file
BIN
designs/stl/box_snap.stl
Normal file
Binary file not shown.
BIN
designs/stl/box_snap_inset_test.stl
Normal file
BIN
designs/stl/box_snap_inset_test.stl
Normal file
Binary file not shown.
BIN
designs/stl/box_test.stl
Normal file
BIN
designs/stl/box_test.stl
Normal file
Binary file not shown.
BIN
designs/stl/dowel.stl
Normal file
BIN
designs/stl/dowel.stl
Normal file
Binary file not shown.
BIN
designs/stl/frame_mount_test.stl
Normal file
BIN
designs/stl/frame_mount_test.stl
Normal file
Binary file not shown.
BIN
designs/stl/front_back_inset.stl
Normal file
BIN
designs/stl/front_back_inset.stl
Normal file
Binary file not shown.
BIN
designs/stl/front_face.stl
Normal file
BIN
designs/stl/front_face.stl
Normal file
Binary file not shown.
BIN
designs/stl/hub.stl
Normal file
BIN
designs/stl/hub.stl
Normal file
Binary file not shown.
BIN
designs/stl/hub_3_port.stl
Normal file
BIN
designs/stl/hub_3_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/hub_4_port.stl
Normal file
BIN
designs/stl/hub_4_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/hub_5_port.stl
Normal file
BIN
designs/stl/hub_5_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/hub_5_port_alt.stl
Normal file
BIN
designs/stl/hub_5_port_alt.stl
Normal file
Binary file not shown.
BIN
designs/stl/hub_6_port.stl
Normal file
BIN
designs/stl/hub_6_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/hub_6_port_alt.stl
Normal file
BIN
designs/stl/hub_6_port_alt.stl
Normal file
Binary file not shown.
BIN
designs/stl/hub_augmented.stl
Normal file
BIN
designs/stl/hub_augmented.stl
Normal file
Binary file not shown.
BIN
designs/stl/hub_test.stl
Normal file
BIN
designs/stl/hub_test.stl
Normal file
Binary file not shown.
Binary file not shown.
BIN
designs/stl/motor_frame.stl
Normal file
BIN
designs/stl/motor_frame.stl
Normal file
Binary file not shown.
BIN
designs/stl/motor_hub_150.stl
Normal file
BIN
designs/stl/motor_hub_150.stl
Normal file
Binary file not shown.
BIN
designs/stl/motor_hub_test.stl
Normal file
BIN
designs/stl/motor_hub_test.stl
Normal file
Binary file not shown.
BIN
designs/stl/rotor.stl
Normal file
BIN
designs/stl/rotor.stl
Normal file
Binary file not shown.
BIN
designs/stl/rotor_125.stl
Normal file
BIN
designs/stl/rotor_125.stl
Normal file
Binary file not shown.
BIN
designs/stl/rotor_125_3_port.stl
Normal file
BIN
designs/stl/rotor_125_3_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/rotor_125_4_port.stl
Normal file
BIN
designs/stl/rotor_125_4_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/rotor_125_5_port.stl
Normal file
BIN
designs/stl/rotor_125_5_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/rotor_125_6_port.stl
Normal file
BIN
designs/stl/rotor_125_6_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/rotor_125_7_port.stl
Normal file
BIN
designs/stl/rotor_125_7_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/rotor_150_5_port.stl
Normal file
BIN
designs/stl/rotor_150_5_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/rotor_5_port.stl
Normal file
BIN
designs/stl/rotor_5_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/rotor_6_port.stl
Normal file
BIN
designs/stl/rotor_6_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/rotor_test.stl
Normal file
BIN
designs/stl/rotor_test.stl
Normal file
Binary file not shown.
BIN
designs/stl/side_face.stl
Normal file
BIN
designs/stl/side_face.stl
Normal file
Binary file not shown.
BIN
designs/stl/siren_stator_custom.stl
Normal file
BIN
designs/stl/siren_stator_custom.stl
Normal file
Binary file not shown.
BIN
designs/stl/snap_box.stl
Normal file
BIN
designs/stl/snap_box.stl
Normal file
Binary file not shown.
BIN
designs/stl/snap_box_bottom.stl
Normal file
BIN
designs/stl/snap_box_bottom.stl
Normal file
Binary file not shown.
BIN
designs/stl/snap_box_full.stl
Normal file
BIN
designs/stl/snap_box_full.stl
Normal file
Binary file not shown.
BIN
designs/stl/snap_box_test.stl
Normal file
BIN
designs/stl/snap_box_test.stl
Normal file
Binary file not shown.
BIN
designs/stl/snap_box_top.stl
Normal file
BIN
designs/stl/snap_box_top.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator.stl
Normal file
BIN
designs/stl/stator.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator_125_3_port.stl
Normal file
BIN
designs/stl/stator_125_3_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator_125_4_port.stl
Normal file
BIN
designs/stl/stator_125_4_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator_125_5_port.stl
Normal file
BIN
designs/stl/stator_125_5_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator_125_6_port.stl
Normal file
BIN
designs/stl/stator_125_6_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator_125_7_port.stl
Normal file
BIN
designs/stl/stator_125_7_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator_150_5_port.stl
Normal file
BIN
designs/stl/stator_150_5_port.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator_5_port.stl
Normal file
BIN
designs/stl/stator_5_port.stl
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
designs/stl/stator_bottom_125.stl
Normal file
BIN
designs/stl/stator_bottom_125.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator_bottom_150.stl
Normal file
BIN
designs/stl/stator_bottom_150.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator_bottom_150_creality.stl
Normal file
BIN
designs/stl/stator_bottom_150_creality.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator_fit_test.stl
Normal file
BIN
designs/stl/stator_fit_test.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator_top.stl
Normal file
BIN
designs/stl/stator_top.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator_top_125.stl
Normal file
BIN
designs/stl/stator_top_125.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator_top_150.stl
Normal file
BIN
designs/stl/stator_top_150.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator_top_50.stl
Normal file
BIN
designs/stl/stator_top_50.stl
Normal file
Binary file not shown.
BIN
designs/stl/stator_top_fit_test.stl
Normal file
BIN
designs/stl/stator_top_fit_test.stl
Normal file
Binary file not shown.
BIN
designs/stl/top_face.stl
Normal file
BIN
designs/stl/top_face.stl
Normal file
Binary file not shown.
|
|
@ -9,7 +9,9 @@
|
|||
// Motor instance
|
||||
//BLDCMotor motor = BLDCMotor(7, 0.14, 2450, 0.00003);
|
||||
//BLDCMotor motor = BLDCMotor(7, 0.15, 940, 0.000125);
|
||||
BLDCMotor motor = BLDCMotor(7, 0.14, 850, 0.0000425);
|
||||
//BLDCMotor motor = BLDCMotor(7, 0.14, 850, 0.0000425);
|
||||
//BLDCMotor motor = BLDCMotor(7, 0.14, 950, 0.000012);
|
||||
BLDCMotor motor = BLDCMotor(7, 0.14, 950, 0.000012);
|
||||
BLDCDriver6PWM driver = BLDCDriver6PWM(A_PHASE_UH, A_PHASE_UL, A_PHASE_VH, A_PHASE_VL, A_PHASE_WH, A_PHASE_WL);
|
||||
// Gain calculation shown at https://community.simplefoc.com/t/b-g431b-esc1-current-control/521/21
|
||||
LowsideCurrentSense currentSense = LowsideCurrentSense(0.003f, -64.0f/7.0f, A_OP1_OUT, A_OP2_OUT, A_OP3_OUT);
|
||||
|
|
@ -83,9 +85,13 @@ void setup() {
|
|||
motor.PID_velocity.P = 0.01;
|
||||
motor.PID_velocity.I = 0.004;
|
||||
|
||||
driver.voltage_limit = 10;
|
||||
motor.voltage_limit = 5;
|
||||
//motor.current_limit = 0.2;
|
||||
driver.voltage_limit = 12;
|
||||
motor.voltage_limit = 6;
|
||||
motor.current_limit = 4.5;
|
||||
|
||||
//driver.voltage_limit = 2;
|
||||
//motor.voltage_limit = 1;
|
||||
//motor.current_limit = 2;
|
||||
|
||||
motor.PID_velocity.limit = motor.voltage_limit;
|
||||
|
||||
|
|
@ -97,9 +103,10 @@ void setup() {
|
|||
motor.LPF_velocity.Tf = 0.01;
|
||||
|
||||
// comment out if not needed
|
||||
//motor.useMonitoring(Serial);
|
||||
motor.useMonitoring(Serial);
|
||||
motor.monitor_variables = _MON_CURR_Q;
|
||||
|
||||
//motor.monitor_downsample = 100000;
|
||||
motor.monitor_downsample = 10000;
|
||||
motor.motion_downsample = 1;
|
||||
|
||||
// initialize motor
|
||||
|
|
@ -130,10 +137,9 @@ void loop() {
|
|||
// Motion control function
|
||||
motor.move(targetV);
|
||||
|
||||
|
||||
if (abs(motor.target) < 20.0f && motor.enabled==1)
|
||||
if (abs(motor.target) < 5.0f && motor.enabled==1)
|
||||
motor.disable();
|
||||
if (abs(motor.target) >= 20.0f && motor.enabled==0)
|
||||
if (abs(motor.target) >= 5.0f && motor.enabled==0)
|
||||
motor.enable();
|
||||
|
||||
// function intended to be used with serial plotter to monitor motor variables
|
||||
|
|
@ -152,4 +158,6 @@ void loop() {
|
|||
*/
|
||||
commandESP.run();
|
||||
//commandComp.run();
|
||||
|
||||
motor.monitor();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,377 @@
|
|||
#include "WiFi.h"
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
// Display Libraries
|
||||
#include <Wire.h>
|
||||
#include <SSD1306Ascii.h>
|
||||
#include <SSD1306AsciiWire.h>
|
||||
|
||||
// Other Libraries
|
||||
#include <arduino-timer.h>
|
||||
#include <AiEsp32RotaryEncoder.h>
|
||||
#include <ArduinoOSCWiFi.h>
|
||||
#include <Preferences.h>
|
||||
|
||||
// WiFi stuff
|
||||
const char* ssid = "sirening";
|
||||
const char* pwd = "alarm_11735";
|
||||
int wifiStatus = 1;
|
||||
|
||||
// for ArduinoOSC
|
||||
const char* host = "192.168.4.200";
|
||||
const int settings_port = 54000;
|
||||
const int state_port = 54001;
|
||||
|
||||
// Button Defs
|
||||
bool buttonState1 = false;
|
||||
AiEsp32RotaryEncoder encoderButton1 = AiEsp32RotaryEncoder(34, 35, 32, -1, 4);
|
||||
//bool ampButtonState = false;
|
||||
//AiEsp32RotaryEncoder ampEncoderButton = AiEsp32RotaryEncoder(33, 25, 26, -1, 4);
|
||||
int menuSelect = 0; //0 = freq, 1 = ports, 2 = ip, 3 = wifi
|
||||
|
||||
float frequency = 0;
|
||||
//float amplitude = 0;
|
||||
int ports = 6;
|
||||
int motorIndex = 2;
|
||||
|
||||
// Display Defs
|
||||
#define I2C_ADDRESS 0x3C
|
||||
SSD1306AsciiWire oled;
|
||||
|
||||
// Preferences
|
||||
#define RW_MODE false
|
||||
#define RO_MODE true
|
||||
Preferences prefs;
|
||||
|
||||
HardwareSerial freqSerial(1);
|
||||
|
||||
void killAll() {
|
||||
updateFreq(0.00);
|
||||
OscWiFi.send(host, state_port, "/state", motorIndex, 0);
|
||||
}
|
||||
|
||||
float hzToRads(float freq) {
|
||||
return freq * TWO_PI / ports;
|
||||
}
|
||||
|
||||
float rpmToRads(float rpm) {
|
||||
return (rpm / 60) * TWO_PI;
|
||||
}
|
||||
|
||||
void updateFreq(float val) {
|
||||
frequency = val;
|
||||
encoderButton1.setEncoderValue(frequency * 10.0);
|
||||
freqSerial.print("\nV" + String(hzToRads(frequency)) + "\n");
|
||||
updateDisplayValue(String(frequency));
|
||||
Serial.println("Frequency: " + String(frequency));
|
||||
}
|
||||
|
||||
/*
|
||||
void updateAmp(float val){
|
||||
amplitude = val;
|
||||
ampEncoderButton.setEncoderValue(amplitude * 10.0);
|
||||
ampSerial.print("\nV" + String(hzToRads(amplitude)) + "\n");
|
||||
updateDisplayValue(amplitude);
|
||||
Serial.println("Amplitude: " + String(amplitude));
|
||||
}
|
||||
*/
|
||||
|
||||
void setupPrefs() {
|
||||
|
||||
prefs.begin("prefs", RO_MODE);
|
||||
bool tpInit = prefs.isKey("nvsInit");
|
||||
|
||||
if (tpInit == false) {
|
||||
prefs.end();
|
||||
prefs.begin("prefs", RW_MODE);
|
||||
|
||||
prefs.putUInt("ports", ports);
|
||||
prefs.putUInt("motorIndex", motorIndex);
|
||||
prefs.putUInt("wifiStatus", wifiStatus);
|
||||
prefs.putBool("nvsInit", true);
|
||||
|
||||
prefs.end();
|
||||
prefs.begin("prefs", RO_MODE);
|
||||
}
|
||||
|
||||
ports = prefs.getUInt("ports");
|
||||
motorIndex = prefs.getUInt("motorIndex");
|
||||
wifiStatus = prefs.getUInt("wifiStatus");
|
||||
prefs.end();
|
||||
}
|
||||
|
||||
void setupOSC() {
|
||||
//OscWiFi.publish(host, state_port, "/state", motorIndex, frequency, amplitude)->setFrameRate(10.f);
|
||||
//OscWiFi.publish(host, state_port, "/state", motorIndex, frequency)->setFrameRate(10.f);
|
||||
|
||||
OscWiFi.subscribe(settings_port, "/kill", [](const OscMessage& m) {
|
||||
killAll();
|
||||
});
|
||||
|
||||
OscWiFi.subscribe(settings_port, "/freq", [](const OscMessage& m) {
|
||||
float input = m.arg<float>(0);
|
||||
if (input < 500.0 && input >= 0.0) {
|
||||
updateFreq(input);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IRAM_ATTR readFreqEncoderISR() {
|
||||
encoderButton1.readEncoder_ISR();
|
||||
}
|
||||
|
||||
void setupEncoderButton(AiEsp32RotaryEncoder& eb, char* val) {
|
||||
eb.begin();
|
||||
if (val == "freq") {
|
||||
eb.setup(readFreqEncoderISR);
|
||||
eb.setBoundaries(0, 5000, false);
|
||||
eb.setAcceleration(1000);
|
||||
}
|
||||
}
|
||||
|
||||
//This needs to be more sophisticated. Moving on if it disconnects...
|
||||
void initWiFi() {
|
||||
|
||||
IPAddress ip(192, 168, 4, 200 + motorIndex);
|
||||
IPAddress gateway(192, 168, 4, 1);
|
||||
IPAddress subnet(255, 255, 0, 0);
|
||||
|
||||
int attempts = 0;
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, pwd);
|
||||
WiFi.config(ip, gateway, subnet);
|
||||
updateDisplayWiFi("Connecting to WiFi", "");
|
||||
//Serial.print("Connecting to WiFi");
|
||||
while (WiFi.status() != WL_CONNECTED && attempts < 5) {
|
||||
//Serial.print('.');
|
||||
attempts += 1;
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
updateDisplayWiFi("Could not connect", "");
|
||||
//Serial.print("Could not Connect");
|
||||
delay(1000);
|
||||
//WiFi.disconnect(true);
|
||||
//WiFi.mode(WIFI_OFF);
|
||||
}
|
||||
Serial.println(WiFi.localIP());
|
||||
}
|
||||
|
||||
void disconnectWiFi() {
|
||||
WiFi.disconnect(true); // Disconnect and clear credentials
|
||||
//WiFi.mode(WIFI_OFF); // Turn off WiFi radio
|
||||
delay(100); // Small delay to ensure disconnection
|
||||
updateDisplayWiFi("DISCONNECTED", "connect");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
freqSerial.begin(115200, SERIAL_8N1, 18, 19);
|
||||
delay(2000);
|
||||
|
||||
setupPrefs();
|
||||
|
||||
setupEncoderButton(encoderButton1, "freq");
|
||||
|
||||
Wire.begin();
|
||||
oled.begin(&Adafruit128x64, I2C_ADDRESS);
|
||||
//oled.setI2cClock(100000L);
|
||||
oled.setFont(System5x7);
|
||||
oled.clear();
|
||||
|
||||
if (wifiStatus == 1) {
|
||||
initWiFi();
|
||||
setupOSC();
|
||||
}
|
||||
|
||||
updateDisplayMenu("Frequency");
|
||||
oled.setCursor(4, 3);
|
||||
oled.println("------------------");
|
||||
updateDisplayValue("0.00");
|
||||
}
|
||||
|
||||
void encoderButtonUpdate(AiEsp32RotaryEncoder& eb, bool& buttonState) {
|
||||
if (eb.encoderChanged()) {
|
||||
oled.setCursor(12, 5);
|
||||
if (menuSelect == 0) {
|
||||
frequency = eb.readEncoder() / 10.0;
|
||||
freqSerial.print("\nV" + String(hzToRads(frequency)) + "\n");
|
||||
OscWiFi.send(host, state_port, "/state", motorIndex, frequency);
|
||||
updateDisplayValue(String(frequency));
|
||||
Serial.println("Frequency: " + String(frequency));
|
||||
}
|
||||
if (menuSelect == 1) {
|
||||
ports = eb.readEncoder();
|
||||
prefs.begin("prefs", RW_MODE);
|
||||
prefs.putUInt("ports", ports);
|
||||
prefs.end();
|
||||
updateDisplayValue(String(ports));
|
||||
Serial.println("No. of Ports: " + String(ports));
|
||||
}
|
||||
if (menuSelect == 2) {
|
||||
motorIndex = eb.readEncoder();
|
||||
prefs.begin("prefs", RW_MODE);
|
||||
prefs.putUInt("motorIndex", motorIndex);
|
||||
prefs.end();
|
||||
updateDisplayValue(String(motorIndex));
|
||||
Serial.println("Siren Index: " + String(motorIndex));
|
||||
}
|
||||
if (menuSelect == 3) {
|
||||
wifiStatus = eb.readEncoder();
|
||||
prefs.begin("prefs", RW_MODE);
|
||||
if (wifiStatus == 1) {
|
||||
initWiFi();
|
||||
setupOSC();
|
||||
prefs.putUInt("wifiStatus", wifiStatus);
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
eb.setEncoderValue(0);
|
||||
}
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
eb.setEncoderValue(0);
|
||||
prefs.putUInt("wifiStatus", 0);
|
||||
|
||||
updateDisplayWiFi("DISCONNECTED", "connect");
|
||||
|
||||
//WiFi.disconnect(true);
|
||||
//WiFi.mode(WIFI_OFF);
|
||||
} else {
|
||||
updateDisplayWiFi("disconnect", "CONNECTED");
|
||||
}
|
||||
} else {
|
||||
prefs.putUInt("wifiStatus", wifiStatus);
|
||||
disconnectWiFi();
|
||||
eb.setEncoderValue(0);
|
||||
}
|
||||
prefs.end();
|
||||
}
|
||||
}
|
||||
|
||||
if (eb.isEncoderButtonDown() && !buttonState) {
|
||||
buttonState = true;
|
||||
}
|
||||
|
||||
//button is up
|
||||
if (!eb.isEncoderButtonDown() && buttonState) {
|
||||
prefs.begin("prefs", RW_MODE);
|
||||
Serial.println(prefs.getUInt("motorIndex", motorIndex));
|
||||
prefs.end();
|
||||
if (frequency > 0) {
|
||||
killAll();
|
||||
} else {
|
||||
menuSelect = (menuSelect + 1) % 4;
|
||||
if (menuSelect == 0) {
|
||||
updateDisplayMenu("Frequency");
|
||||
eb.setBoundaries(0, 5000, false);
|
||||
eb.setAcceleration(1000);
|
||||
eb.setEncoderValue(frequency * 10.0);
|
||||
updateDisplayValue(String(frequency));
|
||||
Serial.println("Frequency: " + String(frequency));
|
||||
}
|
||||
if (menuSelect == 1) {
|
||||
updateDisplayMenu("No. of Ports");
|
||||
eb.setBoundaries(2, 12, false);
|
||||
eb.setAcceleration(1);
|
||||
eb.setEncoderValue(ports);
|
||||
updateDisplayValue(String(ports));
|
||||
Serial.println("No. of Ports: " + String(ports));
|
||||
}
|
||||
if (menuSelect == 2) {
|
||||
updateDisplayMenu("Siren Index");
|
||||
eb.setBoundaries(1, 8, false);
|
||||
eb.setAcceleration(1);
|
||||
eb.setEncoderValue(motorIndex);
|
||||
updateDisplayValue(String(motorIndex));
|
||||
Serial.println("Motor Index: " + String(motorIndex));
|
||||
}
|
||||
if (menuSelect == 3) {
|
||||
updateDisplayMenu("WiFi");
|
||||
eb.setBoundaries(0, 1, false);
|
||||
eb.setAcceleration(1);
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
eb.setEncoderValue(0);
|
||||
updateDisplayWiFi("DISCONNECTED", "connect");
|
||||
} else {
|
||||
eb.setEncoderValue(1);
|
||||
updateDisplayWiFi("disconnect", "CONNECTED");
|
||||
}
|
||||
}
|
||||
}
|
||||
buttonState = false;
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
encoderButtonUpdate(encoderButton1, buttonState1);
|
||||
//encoderButtonUpdate("amp", amplitude, ampEncoderButton, ampButtonState, ampSerial);
|
||||
|
||||
OscWiFi.update();
|
||||
|
||||
//getSerialData();
|
||||
//getFreqSerialData();
|
||||
//getAmpSerialData();
|
||||
}
|
||||
|
||||
void updateDisplayMenu(String string) {
|
||||
oled.set1X();
|
||||
oled.setCursor(4, 2);
|
||||
oled.print(string);
|
||||
oled.clearToEOL();
|
||||
oled.println();
|
||||
}
|
||||
|
||||
void updateDisplayValue(String string) {
|
||||
oled.set2X();
|
||||
oled.setCursor(12, 5);
|
||||
oled.print(string);
|
||||
oled.clearToEOL();
|
||||
oled.println();
|
||||
oled.clearToEOL();
|
||||
oled.println();
|
||||
}
|
||||
|
||||
void updateDisplayWiFi(String string1, String string2) {
|
||||
oled.set1X();
|
||||
oled.setCursor(12, 5);
|
||||
oled.print(string1);
|
||||
oled.clearToEOL();
|
||||
oled.println();
|
||||
oled.clearToEOL();
|
||||
oled.println();
|
||||
oled.setCursor(12, 7);
|
||||
oled.print(string2);
|
||||
oled.clearToEOL();
|
||||
oled.println();
|
||||
}
|
||||
|
||||
bool showSerialData(void*) {
|
||||
|
||||
Serial.print("\nrpm/freq real: ");
|
||||
Serial.print(frequency);
|
||||
Serial.print("\t");
|
||||
Serial.print(frequency / 60 * ports);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void getFreqSerialData() {
|
||||
while (freqSerial.available()) {
|
||||
Serial.print(char(freqSerial.read()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void getSerialData() {
|
||||
if (Serial.available() > 0) {
|
||||
// ignore line feed and carriage return
|
||||
if (Serial.peek() == '\n' || Serial.peek() == '\r') {
|
||||
Serial.read(); // read and discard LF & CR
|
||||
} else {
|
||||
frequency = Serial.parseFloat();
|
||||
Serial.println(frequency, 4); // print to 4 decimal places
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
#include "WiFi.h"
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
// Display Libraries
|
||||
#include <Wire.h>
|
||||
#include <SSD1306Ascii.h>
|
||||
#include <SSD1306AsciiWire.h>
|
||||
|
||||
// Other Libraries
|
||||
#include <arduino-timer.h>
|
||||
#include <AiEsp32RotaryEncoder.h>
|
||||
#include <ArduinoOSCWiFi.h>
|
||||
|
||||
const int sirenCount = 4;
|
||||
|
||||
// WiFi stuff
|
||||
const char* ssid = "sirening";
|
||||
const char* pwd = "alarm_11735";
|
||||
const IPAddress ip(192, 168, 4, 200);
|
||||
const IPAddress gateway(192, 168, 4, 1);
|
||||
const IPAddress subnet(255, 255, 0, 0);
|
||||
|
||||
// for ArduinoOSCo
|
||||
String hosts[sirenCount];
|
||||
const int settings_port = 54000;
|
||||
const int state_port = 54001;
|
||||
|
||||
// Button Defs
|
||||
bool buttonState1 = false;
|
||||
unsigned long startTimeButtonDown = 0;
|
||||
unsigned int button1Count = 0;
|
||||
AiEsp32RotaryEncoder encoderButton1 = AiEsp32RotaryEncoder(34, 35, 32, -1, 4);
|
||||
//bool ampButtonState = false;
|
||||
//AiEsp32RotaryEncoder ampEncoderButton = AiEsp32RotaryEncoder(33, 25, 26, -1, 4);
|
||||
int sirenSelect = 0;
|
||||
|
||||
//TODO: These should be bound to a publication of the frequency directly from the siren
|
||||
float frequencies[sirenCount] = {0};
|
||||
//float frequencyTargets[] = {0, 0, 0};
|
||||
//float amplitudes[] = {0, 0, 0};
|
||||
|
||||
// Display Defs
|
||||
#define I2C_ADDRESS 0x3C
|
||||
SSD1306AsciiWire oled;
|
||||
|
||||
void killAll() {
|
||||
for (int h = 0; h < sirenCount; h++) {
|
||||
OscWiFi.send(hosts[h], settings_port, "/kill");
|
||||
frequencies[h] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void setupOSC(char* path) {
|
||||
OscWiFi.subscribe(state_port, path, [](const OscMessage& m) {
|
||||
int motorIndex = m.arg<int>(0) - 1;
|
||||
float frequency = m.arg<float>(1);
|
||||
if (frequency != frequencies[motorIndex]) {
|
||||
frequencies[motorIndex] = frequency;
|
||||
encoderButton1.setEncoderValue(frequency * 10);
|
||||
if (sirenSelect == motorIndex){
|
||||
updateDisplayValue(String(frequency));
|
||||
}
|
||||
Serial.println("Frequency: " + String(frequency));
|
||||
}
|
||||
/*
|
||||
if(frequency != frequencyTargets[motorIndex]){
|
||||
OscWiFi.send(hosts[sirenSelect], settings_port, "/freq", frequencyTargets[sirenSelect]);
|
||||
}
|
||||
*/
|
||||
});
|
||||
OscWiFi.subscribe(state_port, "/freq", [](const OscMessage& m) {
|
||||
int siren = m.arg<int>(0) - 1;
|
||||
float input = m.arg<float>(1);
|
||||
if (input < 500.0 && input >= 0.0) {
|
||||
frequencies[siren] = input;
|
||||
OscWiFi.send(hosts[siren], settings_port, "/freq", frequencies[siren]);
|
||||
Serial.println("Frequency: " + String(frequencies[siren]));
|
||||
|
||||
encoderButton1.setEncoderValue(frequencies[sirenSelect] * 10.0);
|
||||
updateDisplayValue(String(frequencies[sirenSelect]));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IRAM_ATTR readFreqEncoderISR() {
|
||||
encoderButton1.readEncoder_ISR();
|
||||
}
|
||||
|
||||
/*
|
||||
void IRAM_ATTR readAmpEncoderISR() {
|
||||
ampEncoderButton.readEncoder_ISR();
|
||||
}
|
||||
*/
|
||||
|
||||
void setupEncoderButton(AiEsp32RotaryEncoder& eb, char* val) {
|
||||
eb.begin();
|
||||
if (val == "freq") {
|
||||
eb.setup(readFreqEncoderISR);
|
||||
eb.setBoundaries(0, 5000, false);
|
||||
eb.setAcceleration(1000);
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
||||
delay(2000);
|
||||
|
||||
// initialize serial communication at 115200 bits per second:
|
||||
Serial.begin(115200); //used for debugging.
|
||||
delay(2000);
|
||||
|
||||
for (int i = 0; i < sirenCount; i++) {
|
||||
hosts[i] = "192.168.4." + String(200 + i + 1);
|
||||
Serial.println(hosts[i]);
|
||||
}
|
||||
|
||||
WiFi.softAPConfig(ip, gateway, subnet);
|
||||
WiFi.softAP(ssid, pwd, 1, false, 5);
|
||||
|
||||
/*
|
||||
Serial.println("");
|
||||
Serial.print("WiFi AP IP = ");
|
||||
Serial.println(WiFi.softAPIP());
|
||||
*/
|
||||
|
||||
setupOSC("/state");
|
||||
|
||||
setupEncoderButton(encoderButton1, "freq");
|
||||
//setupEncoderButton(ampEncoderButton, "amp");
|
||||
|
||||
Wire.begin();
|
||||
oled.begin(&Adafruit128x64, I2C_ADDRESS);
|
||||
//oled.setI2cClock(100000L);
|
||||
oled.setFont(System5x7);
|
||||
oled.clear();
|
||||
|
||||
updateDisplayMenu("Frequency (Siren 1)");
|
||||
oled.setCursor(4, 3);
|
||||
oled.println("------------------");
|
||||
updateDisplayValue("0.00");
|
||||
}
|
||||
|
||||
void encoderButtonUpdate(AiEsp32RotaryEncoder& eb, bool& buttonState, char* val) {
|
||||
|
||||
if((millis() - startTimeButtonDown) > 500){
|
||||
button1Count = 0;
|
||||
}
|
||||
|
||||
if (eb.encoderChanged()) {
|
||||
frequencies[sirenSelect] = eb.readEncoder() / 10.0;
|
||||
OscWiFi.send(hosts[sirenSelect], settings_port, "/" + String(val), frequencies[sirenSelect]);
|
||||
updateDisplayValue(String(frequencies[sirenSelect]));
|
||||
Serial.println("Frequency: " + String(frequencies[sirenSelect]));
|
||||
}
|
||||
|
||||
if (eb.isEncoderButtonDown() && !buttonState) {
|
||||
buttonState = true;
|
||||
if(button1Count == 0) {
|
||||
startTimeButtonDown = millis();
|
||||
}
|
||||
button1Count = button1Count + 1;
|
||||
if(button1Count == 2 && (millis() - startTimeButtonDown <= 500)){
|
||||
killAll();
|
||||
sirenSelect = -1;//(sirenSelect - 2) % 3;
|
||||
}
|
||||
}
|
||||
|
||||
//button is up
|
||||
if (!eb.isEncoderButtonDown() && buttonState) {
|
||||
if(val == "freq"){
|
||||
sirenSelect = (sirenSelect + 1) % sirenCount;
|
||||
updateDisplayMenu("Frequency (Siren " + String((sirenSelect+1)) + ")");
|
||||
encoderButton1.setEncoderValue(frequencies[sirenSelect] * 10.0);
|
||||
//ampEncoderButton.setEncoderValue(amplitudes[sirenSelect] * 10.0);
|
||||
}
|
||||
updateDisplayValue(String(frequencies[sirenSelect]));
|
||||
Serial.println("Frequency: " + String(frequencies[sirenSelect]));
|
||||
buttonState = false;
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
encoderButtonUpdate(encoderButton1, buttonState1, "freq");
|
||||
//encoderButtonUpdate(ampEncoderButton, ampButtonState, "amp");
|
||||
|
||||
OscWiFi.update();
|
||||
}
|
||||
|
||||
void updateDisplayMenu(String string) {
|
||||
oled.set1X();
|
||||
oled.setCursor(4, 2);
|
||||
oled.print(string);
|
||||
oled.clearToEOL();
|
||||
oled.println();
|
||||
}
|
||||
|
||||
void updateDisplayValue(String string) {
|
||||
oled.set2X();
|
||||
oled.setCursor(12, 5);
|
||||
oled.print(string);
|
||||
oled.clearToEOL();
|
||||
oled.println();
|
||||
oled.clearToEOL();
|
||||
oled.println();
|
||||
}
|
||||
|
||||
bool showSerialData(void*) {
|
||||
|
||||
Serial.print("\nrpm/freq real: ");
|
||||
Serial.print(frequencies[sirenSelect]);
|
||||
//Serial.print("\t");
|
||||
//Serial.print(frequencies[sirenSelect] / 60 * ports);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
void getSerialData() {
|
||||
if (Serial.available() > 0) {
|
||||
// ignore line feed and carriage return
|
||||
if (Serial.peek() == '\n' || Serial.peek() == '\r') {
|
||||
Serial.read(); // read and discard LF & CR
|
||||
} else {
|
||||
frequency[sirenSelect] = Serial.parseFloat();
|
||||
Serial.println(frequency[sirenSelect], 4); // print to 4 decimal places
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
0
v1/README.md
Normal file
0
v1/README.md
Normal file
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue