diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..396b585 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..ebd9cab --- /dev/null +++ b/AGENTS.md @@ -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 # 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 +#include + +// 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(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 diff --git a/README.md b/README.md index e69de29..1053f5d 100644 --- a/README.md +++ b/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 | diff --git a/designs/3mf/base.3mf b/designs/3mf/base.3mf new file mode 100644 index 0000000..0faf37a Binary files /dev/null and b/designs/3mf/base.3mf differ diff --git a/designs/3mf/hub.3mf b/designs/3mf/hub.3mf new file mode 100644 index 0000000..f770d39 Binary files /dev/null and b/designs/3mf/hub.3mf differ diff --git a/designs/3mf/hub_creality.3mf b/designs/3mf/hub_creality.3mf new file mode 100644 index 0000000..05f3cfd Binary files /dev/null and b/designs/3mf/hub_creality.3mf differ diff --git a/designs/3mf/leg.3mf b/designs/3mf/leg.3mf new file mode 100644 index 0000000..f4c513d Binary files /dev/null and b/designs/3mf/leg.3mf differ diff --git a/designs/3mf/leg_creality.3mf b/designs/3mf/leg_creality.3mf new file mode 100644 index 0000000..5e614b7 Binary files /dev/null and b/designs/3mf/leg_creality.3mf differ diff --git a/designs/3mf/leg_top_creality.3mf b/designs/3mf/leg_top_creality.3mf new file mode 100644 index 0000000..b3d6d50 Binary files /dev/null and b/designs/3mf/leg_top_creality.3mf differ diff --git a/designs/3mf/motor_frame.3mf b/designs/3mf/motor_frame.3mf new file mode 100644 index 0000000..546d822 Binary files /dev/null and b/designs/3mf/motor_frame.3mf differ diff --git a/designs/3mf/rotor.3mf b/designs/3mf/rotor.3mf new file mode 100644 index 0000000..d475d03 Binary files /dev/null and b/designs/3mf/rotor.3mf differ diff --git a/designs/3mf/rotor_creality.3mf b/designs/3mf/rotor_creality.3mf new file mode 100644 index 0000000..d64f205 Binary files /dev/null and b/designs/3mf/rotor_creality.3mf differ diff --git a/designs/3mf/stator.3mf b/designs/3mf/stator.3mf new file mode 100644 index 0000000..952d3f9 Binary files /dev/null and b/designs/3mf/stator.3mf differ diff --git a/designs/3mf/stator_bottom.3mf b/designs/3mf/stator_bottom.3mf new file mode 100644 index 0000000..0a4e411 Binary files /dev/null and b/designs/3mf/stator_bottom.3mf differ diff --git a/designs/3mf/stator_creality.3mf b/designs/3mf/stator_creality.3mf new file mode 100644 index 0000000..972ebf0 Binary files /dev/null and b/designs/3mf/stator_creality.3mf differ diff --git a/designs/3mf/stator_top.3mf b/designs/3mf/stator_top.3mf new file mode 100644 index 0000000..d179923 Binary files /dev/null and b/designs/3mf/stator_top.3mf differ diff --git a/designs/3mf/stator_top_creality.3mf b/designs/3mf/stator_top_creality.3mf new file mode 100644 index 0000000..ae04f80 Binary files /dev/null and b/designs/3mf/stator_top_creality.3mf differ diff --git a/designs/openscad/box_snap.scad b/designs/openscad/box_snap.scad new file mode 100644 index 0000000..efec59d --- /dev/null +++ b/designs/openscad/box_snap.scad @@ -0,0 +1,331 @@ +include + +$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(); diff --git a/designs/openscad/siren_redesign.scad b/designs/openscad/siren_redesign.scad new file mode 100644 index 0000000..95d32c6 --- /dev/null +++ b/designs/openscad/siren_redesign.scad @@ -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(); +*/ + + diff --git a/designs/openscad/stator_iris_top.scad b/designs/openscad/stator_iris_top.scad new file mode 100644 index 0000000..078a559 --- /dev/null +++ b/designs/openscad/stator_iris_top.scad @@ -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); +*/ + + + diff --git a/designs/openscad/tests/bezier_tests.scad b/designs/openscad/tests/bezier_tests.scad new file mode 100644 index 0000000..448f409 --- /dev/null +++ b/designs/openscad/tests/bezier_tests.scad @@ -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(); diff --git a/designs/openscad/tests/blade_work.scad b/designs/openscad/tests/blade_work.scad new file mode 100644 index 0000000..107c322 --- /dev/null +++ b/designs/openscad/tests/blade_work.scad @@ -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(); \ No newline at end of file diff --git a/designs/openscad/tests/box.scad b/designs/openscad/tests/box.scad new file mode 100644 index 0000000..cad52ef --- /dev/null +++ b/designs/openscad/tests/box.scad @@ -0,0 +1,166 @@ +include + +$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); + + + + diff --git a/designs/openscad/tests/impeller.scad b/designs/openscad/tests/impeller.scad new file mode 100644 index 0000000..c556f88 --- /dev/null +++ b/designs/openscad/tests/impeller.scad @@ -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(); diff --git a/designs/openscad/tests/minkowski_play.scad b/designs/openscad/tests/minkowski_play.scad new file mode 100644 index 0000000..ee1e295 --- /dev/null +++ b/designs/openscad/tests/minkowski_play.scad @@ -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); + } +} +*/ + diff --git a/designs/openscad/tests/polyroundtest.scad b/designs/openscad/tests/polyroundtest.scad new file mode 100644 index 0000000..9d48fb5 --- /dev/null +++ b/designs/openscad/tests/polyroundtest.scad @@ -0,0 +1,31 @@ +include + +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]); diff --git a/designs/openscad/tests/siren_simple.scad b/designs/openscad/tests/siren_simple.scad new file mode 100644 index 0000000..a9ee18a --- /dev/null +++ b/designs/openscad/tests/siren_simple.scad @@ -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); +} +*/ + diff --git a/designs/openscad/tests/siren_stator.scad b/designs/openscad/tests/siren_stator.scad new file mode 100644 index 0000000..ce1c792 --- /dev/null +++ b/designs/openscad/tests/siren_stator.scad @@ -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(); diff --git a/designs/openscad/tests/siren_stator_custom.scad b/designs/openscad/tests/siren_stator_custom.scad new file mode 100644 index 0000000..fafed3c --- /dev/null +++ b/designs/openscad/tests/siren_stator_custom.scad @@ -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(); +*/ diff --git a/designs/stl/back_face.stl b/designs/stl/back_face.stl new file mode 100644 index 0000000..2ebe385 Binary files /dev/null and b/designs/stl/back_face.stl differ diff --git a/designs/stl/base.stl b/designs/stl/base.stl new file mode 100644 index 0000000..284422e Binary files /dev/null and b/designs/stl/base.stl differ diff --git a/designs/stl/base_125.stl b/designs/stl/base_125.stl new file mode 100644 index 0000000..41d2669 Binary files /dev/null and b/designs/stl/base_125.stl differ diff --git a/designs/stl/bottom_face.stl b/designs/stl/bottom_face.stl new file mode 100644 index 0000000..18ba26b Binary files /dev/null and b/designs/stl/bottom_face.stl differ diff --git a/designs/stl/bottom_face_test.stl b/designs/stl/bottom_face_test.stl new file mode 100644 index 0000000..1ed4489 Binary files /dev/null and b/designs/stl/bottom_face_test.stl differ diff --git a/designs/stl/box_snap.stl b/designs/stl/box_snap.stl new file mode 100644 index 0000000..432c03f Binary files /dev/null and b/designs/stl/box_snap.stl differ diff --git a/designs/stl/box_snap_inset_test.stl b/designs/stl/box_snap_inset_test.stl new file mode 100644 index 0000000..a29dcf0 Binary files /dev/null and b/designs/stl/box_snap_inset_test.stl differ diff --git a/designs/stl/box_test.stl b/designs/stl/box_test.stl new file mode 100644 index 0000000..4552f0e Binary files /dev/null and b/designs/stl/box_test.stl differ diff --git a/designs/stl/dowel.stl b/designs/stl/dowel.stl new file mode 100644 index 0000000..b120ab8 Binary files /dev/null and b/designs/stl/dowel.stl differ diff --git a/designs/stl/frame_mount_test.stl b/designs/stl/frame_mount_test.stl new file mode 100644 index 0000000..bbde297 Binary files /dev/null and b/designs/stl/frame_mount_test.stl differ diff --git a/designs/stl/front_back_inset.stl b/designs/stl/front_back_inset.stl new file mode 100644 index 0000000..a929fd2 Binary files /dev/null and b/designs/stl/front_back_inset.stl differ diff --git a/designs/stl/front_face.stl b/designs/stl/front_face.stl new file mode 100644 index 0000000..ea0bdaf Binary files /dev/null and b/designs/stl/front_face.stl differ diff --git a/designs/stl/hub.stl b/designs/stl/hub.stl new file mode 100644 index 0000000..a492b99 Binary files /dev/null and b/designs/stl/hub.stl differ diff --git a/designs/stl/hub_3_port.stl b/designs/stl/hub_3_port.stl new file mode 100644 index 0000000..425c66b Binary files /dev/null and b/designs/stl/hub_3_port.stl differ diff --git a/designs/stl/hub_4_port.stl b/designs/stl/hub_4_port.stl new file mode 100644 index 0000000..093958f Binary files /dev/null and b/designs/stl/hub_4_port.stl differ diff --git a/designs/stl/hub_5_port.stl b/designs/stl/hub_5_port.stl new file mode 100644 index 0000000..15a49ed Binary files /dev/null and b/designs/stl/hub_5_port.stl differ diff --git a/designs/stl/hub_5_port_alt.stl b/designs/stl/hub_5_port_alt.stl new file mode 100644 index 0000000..15a49ed Binary files /dev/null and b/designs/stl/hub_5_port_alt.stl differ diff --git a/designs/stl/hub_6_port.stl b/designs/stl/hub_6_port.stl new file mode 100644 index 0000000..67afe09 Binary files /dev/null and b/designs/stl/hub_6_port.stl differ diff --git a/designs/stl/hub_6_port_alt.stl b/designs/stl/hub_6_port_alt.stl new file mode 100644 index 0000000..67afe09 Binary files /dev/null and b/designs/stl/hub_6_port_alt.stl differ diff --git a/designs/stl/hub_augmented.stl b/designs/stl/hub_augmented.stl new file mode 100644 index 0000000..69d6cfb Binary files /dev/null and b/designs/stl/hub_augmented.stl differ diff --git a/designs/stl/hub_test.stl b/designs/stl/hub_test.stl new file mode 100644 index 0000000..b11a042 Binary files /dev/null and b/designs/stl/hub_test.stl differ diff --git a/designs/stl/leg.stl b/designs/stl/leg.stl index 812f88b..a2aeb78 100644 Binary files a/designs/stl/leg.stl and b/designs/stl/leg.stl differ diff --git a/designs/stl/motor_frame.stl b/designs/stl/motor_frame.stl new file mode 100644 index 0000000..4253dc7 Binary files /dev/null and b/designs/stl/motor_frame.stl differ diff --git a/designs/stl/motor_hub_150.stl b/designs/stl/motor_hub_150.stl new file mode 100644 index 0000000..fd27a83 Binary files /dev/null and b/designs/stl/motor_hub_150.stl differ diff --git a/designs/stl/motor_hub_test.stl b/designs/stl/motor_hub_test.stl new file mode 100644 index 0000000..0682e0a Binary files /dev/null and b/designs/stl/motor_hub_test.stl differ diff --git a/designs/stl/rotor.stl b/designs/stl/rotor.stl new file mode 100644 index 0000000..f9ff5eb Binary files /dev/null and b/designs/stl/rotor.stl differ diff --git a/designs/stl/rotor_125.stl b/designs/stl/rotor_125.stl new file mode 100644 index 0000000..29e704f Binary files /dev/null and b/designs/stl/rotor_125.stl differ diff --git a/designs/stl/rotor_125_3_port.stl b/designs/stl/rotor_125_3_port.stl new file mode 100644 index 0000000..8847d27 Binary files /dev/null and b/designs/stl/rotor_125_3_port.stl differ diff --git a/designs/stl/rotor_125_4_port.stl b/designs/stl/rotor_125_4_port.stl new file mode 100644 index 0000000..5a10542 Binary files /dev/null and b/designs/stl/rotor_125_4_port.stl differ diff --git a/designs/stl/rotor_125_5_port.stl b/designs/stl/rotor_125_5_port.stl new file mode 100644 index 0000000..29e704f Binary files /dev/null and b/designs/stl/rotor_125_5_port.stl differ diff --git a/designs/stl/rotor_125_6_port.stl b/designs/stl/rotor_125_6_port.stl new file mode 100644 index 0000000..769f664 Binary files /dev/null and b/designs/stl/rotor_125_6_port.stl differ diff --git a/designs/stl/rotor_125_7_port.stl b/designs/stl/rotor_125_7_port.stl new file mode 100644 index 0000000..976d8aa Binary files /dev/null and b/designs/stl/rotor_125_7_port.stl differ diff --git a/designs/stl/rotor_150_5_port.stl b/designs/stl/rotor_150_5_port.stl new file mode 100644 index 0000000..43539c1 Binary files /dev/null and b/designs/stl/rotor_150_5_port.stl differ diff --git a/designs/stl/rotor_5_port.stl b/designs/stl/rotor_5_port.stl new file mode 100644 index 0000000..9a3f074 Binary files /dev/null and b/designs/stl/rotor_5_port.stl differ diff --git a/designs/stl/rotor_6_port.stl b/designs/stl/rotor_6_port.stl new file mode 100644 index 0000000..6e477ef Binary files /dev/null and b/designs/stl/rotor_6_port.stl differ diff --git a/designs/stl/rotor_test.stl b/designs/stl/rotor_test.stl new file mode 100644 index 0000000..ab5d010 Binary files /dev/null and b/designs/stl/rotor_test.stl differ diff --git a/designs/stl/side_face.stl b/designs/stl/side_face.stl new file mode 100644 index 0000000..c7b1f62 Binary files /dev/null and b/designs/stl/side_face.stl differ diff --git a/designs/stl/siren_stator_custom.stl b/designs/stl/siren_stator_custom.stl new file mode 100644 index 0000000..b920a3e Binary files /dev/null and b/designs/stl/siren_stator_custom.stl differ diff --git a/designs/stl/snap_box.stl b/designs/stl/snap_box.stl new file mode 100644 index 0000000..649e349 Binary files /dev/null and b/designs/stl/snap_box.stl differ diff --git a/designs/stl/snap_box_bottom.stl b/designs/stl/snap_box_bottom.stl new file mode 100644 index 0000000..4cfd00b Binary files /dev/null and b/designs/stl/snap_box_bottom.stl differ diff --git a/designs/stl/snap_box_full.stl b/designs/stl/snap_box_full.stl new file mode 100644 index 0000000..9eb9e2a Binary files /dev/null and b/designs/stl/snap_box_full.stl differ diff --git a/designs/stl/snap_box_test.stl b/designs/stl/snap_box_test.stl new file mode 100644 index 0000000..27e0d55 Binary files /dev/null and b/designs/stl/snap_box_test.stl differ diff --git a/designs/stl/snap_box_top.stl b/designs/stl/snap_box_top.stl new file mode 100644 index 0000000..d40a6dc Binary files /dev/null and b/designs/stl/snap_box_top.stl differ diff --git a/designs/stl/stator.stl b/designs/stl/stator.stl new file mode 100644 index 0000000..0ff67f9 Binary files /dev/null and b/designs/stl/stator.stl differ diff --git a/designs/stl/stator_125_3_port.stl b/designs/stl/stator_125_3_port.stl new file mode 100644 index 0000000..d38d165 Binary files /dev/null and b/designs/stl/stator_125_3_port.stl differ diff --git a/designs/stl/stator_125_4_port.stl b/designs/stl/stator_125_4_port.stl new file mode 100644 index 0000000..fe68653 Binary files /dev/null and b/designs/stl/stator_125_4_port.stl differ diff --git a/designs/stl/stator_125_5_port.stl b/designs/stl/stator_125_5_port.stl new file mode 100644 index 0000000..096f238 Binary files /dev/null and b/designs/stl/stator_125_5_port.stl differ diff --git a/designs/stl/stator_125_6_port.stl b/designs/stl/stator_125_6_port.stl new file mode 100644 index 0000000..bf7d4ce Binary files /dev/null and b/designs/stl/stator_125_6_port.stl differ diff --git a/designs/stl/stator_125_7_port.stl b/designs/stl/stator_125_7_port.stl new file mode 100644 index 0000000..8ffee22 Binary files /dev/null and b/designs/stl/stator_125_7_port.stl differ diff --git a/designs/stl/stator_150_5_port.stl b/designs/stl/stator_150_5_port.stl new file mode 100644 index 0000000..63814aa Binary files /dev/null and b/designs/stl/stator_150_5_port.stl differ diff --git a/designs/stl/stator_5_port.stl b/designs/stl/stator_5_port.stl new file mode 100644 index 0000000..d4e8898 Binary files /dev/null and b/designs/stl/stator_5_port.stl differ diff --git a/designs/stl/stator_6_port.stl b/designs/stl/stator_6_port.stl index a078ef7..bff6d73 100644 Binary files a/designs/stl/stator_6_port.stl and b/designs/stl/stator_6_port.stl differ diff --git a/designs/stl/stator_bottom.stl b/designs/stl/stator_bottom.stl index b6d14fc..d55c4fc 100644 Binary files a/designs/stl/stator_bottom.stl and b/designs/stl/stator_bottom.stl differ diff --git a/designs/stl/stator_bottom_125.stl b/designs/stl/stator_bottom_125.stl new file mode 100644 index 0000000..d5892a4 Binary files /dev/null and b/designs/stl/stator_bottom_125.stl differ diff --git a/designs/stl/stator_bottom_150.stl b/designs/stl/stator_bottom_150.stl new file mode 100644 index 0000000..c99778b Binary files /dev/null and b/designs/stl/stator_bottom_150.stl differ diff --git a/designs/stl/stator_bottom_150_creality.stl b/designs/stl/stator_bottom_150_creality.stl new file mode 100644 index 0000000..5b7c1a5 Binary files /dev/null and b/designs/stl/stator_bottom_150_creality.stl differ diff --git a/designs/stl/stator_fit_test.stl b/designs/stl/stator_fit_test.stl new file mode 100644 index 0000000..fbd65fe Binary files /dev/null and b/designs/stl/stator_fit_test.stl differ diff --git a/designs/stl/stator_top.stl b/designs/stl/stator_top.stl new file mode 100644 index 0000000..46a68dc Binary files /dev/null and b/designs/stl/stator_top.stl differ diff --git a/designs/stl/stator_top_125.stl b/designs/stl/stator_top_125.stl new file mode 100644 index 0000000..906bdac Binary files /dev/null and b/designs/stl/stator_top_125.stl differ diff --git a/designs/stl/stator_top_150.stl b/designs/stl/stator_top_150.stl new file mode 100644 index 0000000..2e13516 Binary files /dev/null and b/designs/stl/stator_top_150.stl differ diff --git a/designs/stl/stator_top_50.stl b/designs/stl/stator_top_50.stl new file mode 100644 index 0000000..2783620 Binary files /dev/null and b/designs/stl/stator_top_50.stl differ diff --git a/designs/stl/stator_top_fit_test.stl b/designs/stl/stator_top_fit_test.stl new file mode 100644 index 0000000..c754389 Binary files /dev/null and b/designs/stl/stator_top_fit_test.stl differ diff --git a/designs/stl/top_face.stl b/designs/stl/top_face.stl new file mode 100644 index 0000000..18ecb4c Binary files /dev/null and b/designs/stl/top_face.stl differ diff --git a/src/B_G431B_ESC1_motor_controller/B_G431B_ESC1_motor_controller.ino b/src/B_G431B_ESC1_motor_controller/B_G431B_ESC1_motor_controller.ino index afee101..9feebc6 100644 --- a/src/B_G431B_ESC1_motor_controller/B_G431B_ESC1_motor_controller.ino +++ b/src/B_G431B_ESC1_motor_controller/B_G431B_ESC1_motor_controller.ino @@ -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(); } diff --git a/src/siren_controller_esp32_simple_station_v2_multi/siren_controller_esp32_simple_station_v2_multi.ino b/src/siren_controller_esp32_simple_station_v2_multi/siren_controller_esp32_simple_station_v2_multi.ino new file mode 100644 index 0000000..b1707fa --- /dev/null +++ b/src/siren_controller_esp32_simple_station_v2_multi/siren_controller_esp32_simple_station_v2_multi.ino @@ -0,0 +1,377 @@ +#include "WiFi.h" + +#include + +// Display Libraries +#include +#include +#include + +// Other Libraries +#include +#include +#include +#include + +// 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(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 + } + } +} diff --git a/src/siren_remote_control_esp32_v2/siren_remote_control_esp32_v2.ino b/src/siren_remote_control_esp32_v2/siren_remote_control_esp32_v2.ino new file mode 100644 index 0000000..78bf48d --- /dev/null +++ b/src/siren_remote_control_esp32_v2/siren_remote_control_esp32_v2.ino @@ -0,0 +1,230 @@ +#include "WiFi.h" + +#include + +// Display Libraries +#include +#include +#include + +// Other Libraries +#include +#include +#include + +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(0) - 1; + float frequency = m.arg(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(0) - 1; + float input = m.arg(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 + } + } +} +*/ diff --git a/v1/README.md b/v1/README.md new file mode 100644 index 0000000..e69de29 diff --git a/designs/cad/box_2_no_slats.20250120-200901.FCBak b/v1/designs/cad/box_2_no_slats.20250120-200901.FCBak similarity index 100% rename from designs/cad/box_2_no_slats.20250120-200901.FCBak rename to v1/designs/cad/box_2_no_slats.20250120-200901.FCBak diff --git a/designs/cad/box_2_no_slats.FCStd b/v1/designs/cad/box_2_no_slats.FCStd similarity index 100% rename from designs/cad/box_2_no_slats.FCStd rename to v1/designs/cad/box_2_no_slats.FCStd diff --git a/designs/cad/box_top.FCStd b/v1/designs/cad/box_top.FCStd similarity index 100% rename from designs/cad/box_top.FCStd rename to v1/designs/cad/box_top.FCStd diff --git a/designs/cad/chopper_only_v3.20241126-171000.FCBak b/v1/designs/cad/chopper_only_v3.20241126-171000.FCBak similarity index 100% rename from designs/cad/chopper_only_v3.20241126-171000.FCBak rename to v1/designs/cad/chopper_only_v3.20241126-171000.FCBak diff --git a/designs/cad/chopper_only_v3.FCStd b/v1/designs/cad/chopper_only_v3.FCStd similarity index 100% rename from designs/cad/chopper_only_v3.FCStd rename to v1/designs/cad/chopper_only_v3.FCStd diff --git a/designs/cad/esp_holder.20250118-210844.FCBak b/v1/designs/cad/esp_holder.20250118-210844.FCBak similarity index 100% rename from designs/cad/esp_holder.20250118-210844.FCBak rename to v1/designs/cad/esp_holder.20250118-210844.FCBak diff --git a/designs/cad/esp_holder.FCStd b/v1/designs/cad/esp_holder.FCStd similarity index 100% rename from designs/cad/esp_holder.FCStd rename to v1/designs/cad/esp_holder.FCStd diff --git a/designs/cad/impeller_chopper_v4.FCStd b/v1/designs/cad/impeller_chopper_v4.FCStd similarity index 100% rename from designs/cad/impeller_chopper_v4.FCStd rename to v1/designs/cad/impeller_chopper_v4.FCStd diff --git a/designs/cad/impeller_only_v3.20250117-002623.FCBak b/v1/designs/cad/impeller_only_v3.20250117-002623.FCBak similarity index 100% rename from designs/cad/impeller_only_v3.20250117-002623.FCBak rename to v1/designs/cad/impeller_only_v3.20250117-002623.FCBak diff --git a/designs/cad/impeller_only_v3.FCStd b/v1/designs/cad/impeller_only_v3.FCStd similarity index 100% rename from designs/cad/impeller_only_v3.FCStd rename to v1/designs/cad/impeller_only_v3.FCStd diff --git a/designs/cad/impeller_only_v3_larger.FCStd b/v1/designs/cad/impeller_only_v3_larger.FCStd similarity index 100% rename from designs/cad/impeller_only_v3_larger.FCStd rename to v1/designs/cad/impeller_only_v3_larger.FCStd diff --git a/designs/cad/leg.20250115-131731.FCBak b/v1/designs/cad/leg.20250115-131731.FCBak similarity index 100% rename from designs/cad/leg.20250115-131731.FCBak rename to v1/designs/cad/leg.20250115-131731.FCBak diff --git a/designs/cad/leg.FCStd b/v1/designs/cad/leg.FCStd similarity index 100% rename from designs/cad/leg.FCStd rename to v1/designs/cad/leg.FCStd diff --git a/designs/cad/magnet_holder.20241204-153014.FCBak b/v1/designs/cad/magnet_holder.20241204-153014.FCBak similarity index 100% rename from designs/cad/magnet_holder.20241204-153014.FCBak rename to v1/designs/cad/magnet_holder.20241204-153014.FCBak diff --git a/designs/cad/magnet_holder.FCStd b/v1/designs/cad/magnet_holder.FCStd similarity index 100% rename from designs/cad/magnet_holder.FCStd rename to v1/designs/cad/magnet_holder.FCStd diff --git a/designs/cad/motor_frame.20250103-225514.FCBak b/v1/designs/cad/motor_frame.20250103-225514.FCBak similarity index 100% rename from designs/cad/motor_frame.20250103-225514.FCBak rename to v1/designs/cad/motor_frame.20250103-225514.FCBak diff --git a/designs/cad/motor_frame.FCStd b/v1/designs/cad/motor_frame.FCStd similarity index 100% rename from designs/cad/motor_frame.FCStd rename to v1/designs/cad/motor_frame.FCStd diff --git a/designs/cad/remote.20250124-121518.FCBak b/v1/designs/cad/remote.20250124-121518.FCBak similarity index 100% rename from designs/cad/remote.20250124-121518.FCBak rename to v1/designs/cad/remote.20250124-121518.FCBak diff --git a/designs/cad/remote.20250125-170345.FCBak b/v1/designs/cad/remote.20250125-170345.FCBak similarity index 100% rename from designs/cad/remote.20250125-170345.FCBak rename to v1/designs/cad/remote.20250125-170345.FCBak diff --git a/designs/cad/remote.FCStd b/v1/designs/cad/remote.FCStd similarity index 100% rename from designs/cad/remote.FCStd rename to v1/designs/cad/remote.FCStd diff --git a/designs/cad/remote_box.20250124-134644.FCBak b/v1/designs/cad/remote_box.20250124-134644.FCBak similarity index 100% rename from designs/cad/remote_box.20250124-134644.FCBak rename to v1/designs/cad/remote_box.20250124-134644.FCBak diff --git a/designs/cad/remote_box.20250125-170552.FCBak b/v1/designs/cad/remote_box.20250125-170552.FCBak similarity index 100% rename from designs/cad/remote_box.20250125-170552.FCBak rename to v1/designs/cad/remote_box.20250125-170552.FCBak diff --git a/designs/cad/remote_box.FCStd b/v1/designs/cad/remote_box.FCStd similarity index 100% rename from designs/cad/remote_box.FCStd rename to v1/designs/cad/remote_box.FCStd diff --git a/designs/cad/sensor_frame.20241208-155322.FCBak b/v1/designs/cad/sensor_frame.20241208-155322.FCBak similarity index 100% rename from designs/cad/sensor_frame.20241208-155322.FCBak rename to v1/designs/cad/sensor_frame.20241208-155322.FCBak diff --git a/designs/cad/sensor_frame.FCStd b/v1/designs/cad/sensor_frame.FCStd similarity index 100% rename from designs/cad/sensor_frame.FCStd rename to v1/designs/cad/sensor_frame.FCStd diff --git a/designs/cad/siren.20241126-123307.FCBak b/v1/designs/cad/siren.20241126-123307.FCBak similarity index 100% rename from designs/cad/siren.20241126-123307.FCBak rename to v1/designs/cad/siren.20241126-123307.FCBak diff --git a/designs/cad/siren.FCStd b/v1/designs/cad/siren.FCStd similarity index 100% rename from designs/cad/siren.FCStd rename to v1/designs/cad/siren.FCStd diff --git a/designs/cad/stator_bottom.20241126-123313.FCBak b/v1/designs/cad/stator_bottom.20241126-123313.FCBak similarity index 100% rename from designs/cad/stator_bottom.20241126-123313.FCBak rename to v1/designs/cad/stator_bottom.20241126-123313.FCBak diff --git a/designs/cad/stator_bottom.FCStd b/v1/designs/cad/stator_bottom.FCStd similarity index 100% rename from designs/cad/stator_bottom.FCStd rename to v1/designs/cad/stator_bottom.FCStd diff --git a/designs/cad/stator_cap_v3.20241126-170336.FCBak b/v1/designs/cad/stator_cap_v3.20241126-170336.FCBak similarity index 100% rename from designs/cad/stator_cap_v3.20241126-170336.FCBak rename to v1/designs/cad/stator_cap_v3.20241126-170336.FCBak diff --git a/designs/cad/stator_cap_v3.FCStd b/v1/designs/cad/stator_cap_v3.FCStd similarity index 100% rename from designs/cad/stator_cap_v3.FCStd rename to v1/designs/cad/stator_cap_v3.FCStd diff --git a/designs/cad/stator_cap_v3_more_closed.20250117-001022.FCBak b/v1/designs/cad/stator_cap_v3_more_closed.20250117-001022.FCBak similarity index 100% rename from designs/cad/stator_cap_v3_more_closed.20250117-001022.FCBak rename to v1/designs/cad/stator_cap_v3_more_closed.20250117-001022.FCBak diff --git a/designs/cad/stator_cap_v3_more_closed.FCStd b/v1/designs/cad/stator_cap_v3_more_closed.FCStd similarity index 100% rename from designs/cad/stator_cap_v3_more_closed.FCStd rename to v1/designs/cad/stator_cap_v3_more_closed.FCStd diff --git a/designs/cad/stator_only.20241126-163958.FCBak b/v1/designs/cad/stator_only.20241126-163958.FCBak similarity index 100% rename from designs/cad/stator_only.20241126-163958.FCBak rename to v1/designs/cad/stator_only.20241126-163958.FCBak diff --git a/designs/cad/stator_only.FCStd b/v1/designs/cad/stator_only.FCStd similarity index 100% rename from designs/cad/stator_only.FCStd rename to v1/designs/cad/stator_only.FCStd diff --git a/designs/cad/stm_holder_2.20250114-233937.FCBak b/v1/designs/cad/stm_holder_2.20250114-233937.FCBak similarity index 100% rename from designs/cad/stm_holder_2.20250114-233937.FCBak rename to v1/designs/cad/stm_holder_2.20250114-233937.FCBak diff --git a/designs/cad/stm_holder_2.FCStd b/v1/designs/cad/stm_holder_2.FCStd similarity index 100% rename from designs/cad/stm_holder_2.FCStd rename to v1/designs/cad/stm_holder_2.FCStd diff --git a/designs/stl/box_2_no_slats.stl b/v1/designs/stl/box_2_no_slats.stl similarity index 100% rename from designs/stl/box_2_no_slats.stl rename to v1/designs/stl/box_2_no_slats.stl diff --git a/designs/stl/box_top.stl b/v1/designs/stl/box_top.stl similarity index 100% rename from designs/stl/box_top.stl rename to v1/designs/stl/box_top.stl diff --git a/designs/stl/chopper_only_v3.stl b/v1/designs/stl/chopper_only_v3.stl similarity index 100% rename from designs/stl/chopper_only_v3.stl rename to v1/designs/stl/chopper_only_v3.stl diff --git a/designs/stl/chopper_only_v3_4_port.stl b/v1/designs/stl/chopper_only_v3_4_port.stl similarity index 100% rename from designs/stl/chopper_only_v3_4_port.stl rename to v1/designs/stl/chopper_only_v3_4_port.stl diff --git a/designs/stl/chopper_only_v3_6_port.stl b/v1/designs/stl/chopper_only_v3_6_port.stl similarity index 100% rename from designs/stl/chopper_only_v3_6_port.stl rename to v1/designs/stl/chopper_only_v3_6_port.stl diff --git a/designs/stl/esp_holder.stl b/v1/designs/stl/esp_holder.stl similarity index 100% rename from designs/stl/esp_holder.stl rename to v1/designs/stl/esp_holder.stl diff --git a/designs/stl/impeller_larger_6_blade.stl b/v1/designs/stl/impeller_larger_6_blade.stl similarity index 100% rename from designs/stl/impeller_larger_6_blade.stl rename to v1/designs/stl/impeller_larger_6_blade.stl diff --git a/designs/stl/impeller_only_v3.stl b/v1/designs/stl/impeller_only_v3.stl similarity index 100% rename from designs/stl/impeller_only_v3.stl rename to v1/designs/stl/impeller_only_v3.stl diff --git a/designs/stl/impeller_only_v3_4_blade.stl b/v1/designs/stl/impeller_only_v3_4_blade.stl similarity index 100% rename from designs/stl/impeller_only_v3_4_blade.stl rename to v1/designs/stl/impeller_only_v3_4_blade.stl diff --git a/designs/stl/impeller_only_v3_5_blade.stl b/v1/designs/stl/impeller_only_v3_5_blade.stl similarity index 100% rename from designs/stl/impeller_only_v3_5_blade.stl rename to v1/designs/stl/impeller_only_v3_5_blade.stl diff --git a/designs/stl/impeller_only_v3_6_blade.stl b/v1/designs/stl/impeller_only_v3_6_blade.stl similarity index 100% rename from designs/stl/impeller_only_v3_6_blade.stl rename to v1/designs/stl/impeller_only_v3_6_blade.stl diff --git a/v1/designs/stl/leg.stl b/v1/designs/stl/leg.stl new file mode 100644 index 0000000..812f88b Binary files /dev/null and b/v1/designs/stl/leg.stl differ diff --git a/designs/stl/magnet_holder.stl b/v1/designs/stl/magnet_holder.stl similarity index 100% rename from designs/stl/magnet_holder.stl rename to v1/designs/stl/magnet_holder.stl diff --git a/designs/stl/motor_frame_threaded.stl b/v1/designs/stl/motor_frame_threaded.stl similarity index 100% rename from designs/stl/motor_frame_threaded.stl rename to v1/designs/stl/motor_frame_threaded.stl diff --git a/designs/stl/remote.stl b/v1/designs/stl/remote.stl similarity index 100% rename from designs/stl/remote.stl rename to v1/designs/stl/remote.stl diff --git a/designs/stl/remote_box.stl b/v1/designs/stl/remote_box.stl similarity index 100% rename from designs/stl/remote_box.stl rename to v1/designs/stl/remote_box.stl diff --git a/designs/stl/sensor_frame.stl b/v1/designs/stl/sensor_frame.stl similarity index 100% rename from designs/stl/sensor_frame.stl rename to v1/designs/stl/sensor_frame.stl diff --git a/v1/designs/stl/stator_6_port.stl b/v1/designs/stl/stator_6_port.stl new file mode 100644 index 0000000..a078ef7 Binary files /dev/null and b/v1/designs/stl/stator_6_port.stl differ diff --git a/v1/designs/stl/stator_bottom.stl b/v1/designs/stl/stator_bottom.stl new file mode 100644 index 0000000..b6d14fc Binary files /dev/null and b/v1/designs/stl/stator_bottom.stl differ diff --git a/designs/stl/stator_cap_v3.stl b/v1/designs/stl/stator_cap_v3.stl similarity index 100% rename from designs/stl/stator_cap_v3.stl rename to v1/designs/stl/stator_cap_v3.stl diff --git a/designs/stl/stator_cap_v3_more_closed.stl b/v1/designs/stl/stator_cap_v3_more_closed.stl similarity index 100% rename from designs/stl/stator_cap_v3_more_closed.stl rename to v1/designs/stl/stator_cap_v3_more_closed.stl diff --git a/designs/stl/stator_only.stl b/v1/designs/stl/stator_only.stl similarity index 100% rename from designs/stl/stator_only.stl rename to v1/designs/stl/stator_only.stl diff --git a/designs/stl/stator_only_6_port.stl b/v1/designs/stl/stator_only_6_port.stl similarity index 100% rename from designs/stl/stator_only_6_port.stl rename to v1/designs/stl/stator_only_6_port.stl diff --git a/designs/stl/stator_only_7_port.stl b/v1/designs/stl/stator_only_7_port.stl similarity index 100% rename from designs/stl/stator_only_7_port.stl rename to v1/designs/stl/stator_only_7_port.stl diff --git a/designs/stl/stator_only_v3_4_port.stl b/v1/designs/stl/stator_only_v3_4_port.stl similarity index 100% rename from designs/stl/stator_only_v3_4_port.stl rename to v1/designs/stl/stator_only_v3_4_port.stl diff --git a/designs/stl/stm_holder.stl b/v1/designs/stl/stm_holder.stl similarity index 100% rename from designs/stl/stm_holder.stl rename to v1/designs/stl/stm_holder.stl diff --git a/v1/src/B_G431B_ESC1_motor_controller/B_G431B_ESC1_motor_controller.ino b/v1/src/B_G431B_ESC1_motor_controller/B_G431B_ESC1_motor_controller.ino new file mode 100644 index 0000000..afee101 --- /dev/null +++ b/v1/src/B_G431B_ESC1_motor_controller/B_G431B_ESC1_motor_controller.ino @@ -0,0 +1,155 @@ +/** + * B-G431B-ESC1 position motion control example with encoder + * + */ +#include +#include +#include + +// 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); +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); + +// encoder instance +MXLEMMINGObserverSensor sensor = MXLEMMINGObserverSensor(motor); + +HardwareSerial Serial1(PB6, PB7); + +// velocity set point variable +float target = 0; +float targetP = 0; +float targetI = 0; +float targetV = 0; +// instantiate the commander +Commander commandComp = Commander(Serial); +Commander commandESP = Commander(Serial1); +void doTargetP(char* cmd) { commandComp.scalar(&targetP, cmd); } +void doTargetI(char* cmd) { commandComp.scalar(&targetI, cmd); } +void doTargetV(char* cmd) { commandESP.scalar(&targetV, cmd); } + +void setup() { + + //pinMode(LED_RED, OUTPUT); // Set board LED for debugging + //digitalWrite(LED_RED, HIGH); + + // use monitoring with serial + Serial.begin(115200); + Serial1.begin(115200); + + // enable more verbose output for debugging + // comment out if not needed + //SimpleFOCDebug::enable(&Serial); + + // initialize encoder sensor hardware + sensor.init(); + + // link the motor to the sensor + motor.linkSensor(&sensor); + + // driver config + // power supply voltage [V] + driver.voltage_power_supply = 12; + driver.init(); + // link the motor and the driver + motor.linkDriver(&driver); + // link current sense and the driver + currentSense.linkDriver(&driver); + + // initialise the current sensing + if(!currentSense.init()){ + Serial.println("Current sense init failed."); + return; + } + // no need for aligning + currentSense.skip_align = true; + motor.linkCurrentSense(¤tSense); + + motor.sensor_direction= Direction::CW; + motor.zero_electric_angle = 0; + + // set motion control loop to be used + motor.controller = MotionControlType::velocity; + motor.torque_controller = TorqueControlType::foc_current; + //motor.torque_controller = TorqueControlType::dc_current; + //motor.foc_modulation = FOCModulationType::SpaceVectorPWM; + + motor.current_sp = motor.current_limit; + + // velocity PI controller parameters + 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; + + motor.PID_velocity.limit = motor.voltage_limit; + + // jerk control using voltage voltage ramp + // default value is 300 volts per sec ~ 0.3V per millisecond + motor.PID_velocity.output_ramp = 10; + + // velocity low pass filtering time constant + motor.LPF_velocity.Tf = 0.01; + + // comment out if not needed + //motor.useMonitoring(Serial); + + //motor.monitor_downsample = 100000; + motor.motion_downsample = 1; + + // initialize motor + motor.init(); + // align encoder and start FOC + motor.initFOC(); + + // add target command T + commandComp.add('P', doTargetP, "target P"); + commandComp.add('I', doTargetI, "target I"); + commandESP.add('V', doTargetV, "target V"); + + Serial.println(F("Motor ready.")); + Serial.println(F("Set the target velocity using serial terminal:")); + _delay(1000); +} + +void loop() { + + // main FOC algorithm function + motor.loopFOC(); + + //motor.PID_velocity.P = targetP; + //motor.PID_velocity.I = targetI; + + //Serial.println(target); + + // Motion control function + motor.move(targetV); + + + if (abs(motor.target) < 20.0f && motor.enabled==1) + motor.disable(); + if (abs(motor.target) >= 20.0f && motor.enabled==0) + motor.enable(); + + // function intended to be used with serial plotter to monitor motor variables + // significantly slowing the execution down!!!! + //motor.monitor(); + + // user communication + /* + static uint32_t lastSerialRead = 0; + if (lastSerialRead == 32) { + lastSerialRead = 0; + commandESP.run(); + commandComp.run(); + } + lastSerialRead += 1; + */ + commandESP.run(); + //commandComp.run(); +} diff --git a/v1/src/B_G431B_ESC1_motor_controller/build_opt.h b/v1/src/B_G431B_ESC1_motor_controller/build_opt.h new file mode 100644 index 0000000..82f3ab8 --- /dev/null +++ b/v1/src/B_G431B_ESC1_motor_controller/build_opt.h @@ -0,0 +1,2 @@ +-DHAL_OPAMP_MODULE_ENABLED +-DHAL_UART_MODULE_ENABLED \ No newline at end of file diff --git a/src/siren_controller_esp32_simple_station/siren_controller_esp32_simple_station.ino b/v1/src/siren_controller_esp32_simple_station/siren_controller_esp32_simple_station.ino similarity index 100% rename from src/siren_controller_esp32_simple_station/siren_controller_esp32_simple_station.ino rename to v1/src/siren_controller_esp32_simple_station/siren_controller_esp32_simple_station.ino diff --git a/v1/src/siren_controller_esp32_simple_station_v2/siren_controller_esp32_simple_station_v2.ino b/v1/src/siren_controller_esp32_simple_station_v2/siren_controller_esp32_simple_station_v2.ino new file mode 100644 index 0000000..1a6490e --- /dev/null +++ b/v1/src/siren_controller_esp32_simple_station_v2/siren_controller_esp32_simple_station_v2.ino @@ -0,0 +1,265 @@ +#include "WiFi.h" + +#include + +// Display Libraries +#include +#include +#include + +// Other Libraries +#include +#include +#include +#include + +// WiFi stuff +const char* ssid = "sirening"; +const char* pwd = "alarm_11735"; +const IPAddress ip(192, 168, 4, 203); +const IPAddress gateway(192, 168, 4, 1); +const IPAddress subnet(255, 255, 0, 0); + +// for ArduinoOSC +const char* host = "192.168.4.200"; +const int settings_port = 54000; +const int state_port = 54001; + +// Button Defs +bool freqButtonState = false; +AiEsp32RotaryEncoder freqEncoderButton = AiEsp32RotaryEncoder(34, 35, 32, -1, 4); +//bool ampButtonState = false; +//AiEsp32RotaryEncoder ampEncoderButton = AiEsp32RotaryEncoder(33, 25, 26, -1, 4); +int menuSelect = 0; //0 = freq, 1 = ports + +float frequency = 0; +//float amplitude = 0; +int ports = 6; +const int motorIndex = 2; + +// Display Defs +#define I2C_ADDRESS 0x3C +SSD1306AsciiWire oled; +uint8_t fieldWidth; +uint8_t rowsPerLine; // Rows per line. + +// Preferences +#define RW_MODE false +#define RO_MODE true +Preferences prefs; + +HardwareSerial freqSerial(1); + +void killAll() { + updateFreq(0.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; + Serial.println("new freq is: " + String(frequency)); + freqEncoderButton.setEncoderValue(frequency * 10.0); + freqSerial.print("\nV" + String(hzToRads(frequency)) + "\n"); + updateDisplay(); +} + +/* +void updateAmp(float val){ + amplitude = val; + Serial.println("new amp is: " + String(amplitude)); + ampEncoderButton.setEncoderValue(amplitude * 10.0); + ampSerial.print("\nV" + String(hzToRads(amplitude)) + "\n"); + updateDisplay(); +} +*/ + +void setupPrefs(){ + + prefs.begin("STCPrefs", RO_MODE); // Open our namespace (or create itif it doesn't exist) in RO mode. + + bool tpInit = prefs.isKey("nvsInit"); // Test for the existence of the "already initialized" key. + + if (tpInit == false) { + // If tpInit is 'false', the key "nvsInit" does not yet exist therefore this must be our first-time run. We need to set up our Preferences namespace keys. So... + prefs.end(); // close the namespace in RO mode and... + prefs.begin("prefs", RW_MODE); // reopen it in RW mode. + + + // The .begin() method created the "STCPrefs" namespace and since this is our first-time run we will create our keys and store the initial "factory default" values. + prefs.putUChar("ports", ports); + //prefs.putString("talChan", "one"); + //prefs.putLong("talMax", -220226); + //prefs.putBool("ctMde", true); + + prefs.putBool("nvsInit", true); // Create the "already initialized" + // key and store a value. + + // The "factory defaults" are created and stored so... + prefs.end(); // Close the namespace in RW mode and... + prefs.begin("prefs", RO_MODE); // reopen it in RO mode so the setup code outside this first-time run 'if' block can retrieve the run-time values from the "STCPrefs" namespace. + } + + // Retrieve the operational parameters from the namespace and save them into their run-time variables. + ports = prefs.getUChar("ports"); // + //tChannel = prefs.getString("talChan"); // The LHS variables were defined + //tChanMax = prefs.getLong("talMax"); // earlier in the sketch. + //ctMode = prefs.getBool("ctMde"); // + + // All done. Last run state (or the factory default) is now restored. + 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(0); + if(input < 500.0 && input >= 0.0){ + updateFreq(input); + } + }); +} + +void IRAM_ATTR readFreqEncoderISR() { + freqEncoderButton.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() { + int attempts = 0; + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, pwd); + WiFi.config(ip, gateway, subnet); + Serial.println(); + Serial.print("Connecting to WiFi .."); + while (WiFi.status() != WL_CONNECTED && attempts < 5) { + Serial.print('.'); + attempts += 1; + delay(1000); + } + Serial.println(WiFi.localIP()); +} + +void setup() { + + Serial.begin(115200); + freqSerial.begin(115200, SERIAL_8N1, 18, 19); + delay(2000); + + initWiFi(); + + setupOSC(); + + setupEncoderButton(freqEncoderButton, "freq"); + + Wire.begin(); + oled.begin(&Adafruit128x64, I2C_ADDRESS); + //oled.setI2cClock(100000L); + oled.setFont(System5x7); + oled.clear(); + oled.println(); + oled.println(); + oled.println(" frequency: 0"); + //oled.println(" frequency: 0"); + //oled.print(" amplitude: 0"); + fieldWidth = oled.fieldWidth(strlen(" amplitude: ")); + rowsPerLine = oled.fontRows(); + Serial.println("here"); +} + +void encoderButtonUpdate(char* val, float& setting, AiEsp32RotaryEncoder& eb, bool& buttonState, HardwareSerial& ser) { + if (eb.encoderChanged()) { + if (menuState == 0){ + setting = eb.readEncoder() / 10.0; + ser.print("\nV" + String(hzToRads(setting)) + "\n"); + Serial.println("new " + String(val) + " is: " + String(setting)); + updateDisplay(); + } else if(menuState == 1){ + + } + } + + if (eb.isEncoderButtonDown() && !buttonState) { + buttonState = true; + } + + //button is up + if (!eb.isEncoderButtonDown() && buttonState) { + if (frequency >= 0){ + killAll(); + } else { + menuState = menuState + 1; + } + buttonState = false; + } +} + +void loop() +{ + encoderButtonUpdate("freq", frequency, freqEncoderButton, freqButtonState, freqSerial); + //encoderButtonUpdate("amp", amplitude, ampEncoderButton, ampButtonState, ampSerial); + + OscWiFi.update(); + + getSerialData(); + getFreqSerialData(); + //getAmpSerialData(); +} + +void updateDisplay(){ + oled.clearField(fieldWidth, rowsPerLine * 2, 4); + oled.print(frequency); + //oled.clearField(fieldWidth, rowsPerLine * 3, 4); + //oled.print(amplitude); +} + +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 + } + } +} diff --git a/src/siren_remote_control_esp32/siren_remote_control_esp32.ino b/v1/src/siren_remote_control_esp32/siren_remote_control_esp32.ino similarity index 100% rename from src/siren_remote_control_esp32/siren_remote_control_esp32.ino rename to v1/src/siren_remote_control_esp32/siren_remote_control_esp32.ino