Compare commits
59 Commits
a0cdc5a0e0
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| d8e59ee525 | |||
| 51241512b9 | |||
| 757cfdac8a | |||
| c6348417ec | |||
|
|
c6fd370e02 | ||
|
|
6931ee4dfd | ||
|
|
6aa6a6637f | ||
|
|
a0f8a06daf | ||
| dca1ab0b53 | |||
| fbd14c9b5d | |||
| 303423a5a5 | |||
| f55dab7546 | |||
| b7a0aa47c8 | |||
| eed8f1365d | |||
| 2eebf73208 | |||
| 62b6472b43 | |||
| 5c3f03ecdd | |||
| 3bacff9ea6 | |||
| dda7ee60c0 | |||
| 33ce61cc8a | |||
| ad39e86a9d | |||
| 5a389975b2 | |||
| 7c609ea452 | |||
|
|
ada5370693 | ||
| 91a9794646 | |||
| f22ded6423 | |||
| 4e749580d9 | |||
| 3bb4f8d02d | |||
| 1a9db68067 | |||
| fedeeea9cb | |||
| c9678e16cd | |||
| d94726d056 | |||
| 83a55fcc47 | |||
|
|
3580e32142 | ||
|
|
f44dcb2317 | ||
|
|
d2273c5fea | ||
|
|
b89fd8363c | ||
|
|
7c893c0966 | ||
|
|
db4964636c | ||
|
|
92d821e212 | ||
|
|
803c062e81 | ||
|
|
3d6144bd7f | ||
|
|
c765e4b7c2 | ||
| a1ffcb08ca | |||
|
|
3487d7c263 | ||
|
|
3059e5a493 | ||
|
|
40f1999b25 | ||
| a23ca75d92 | |||
| 556c7888de | |||
| c1bc52c5a3 | |||
| e4e3a22924 | |||
|
|
fc868377d2 | ||
|
|
076b5ba3e5 | ||
|
|
53e51dc50b | ||
|
|
42e9c4d97b | ||
|
|
098c577142 | ||
| 50430b050e | |||
|
|
cf4c2c6b64 | ||
|
|
aa35075bd2 |
72
.gitea/workflows/deploy-ftp.yml
Normal file
72
.gitea/workflows/deploy-ftp.yml
Normal file
@@ -0,0 +1,72 @@
|
||||
name: Deploy firmware via FTP (master)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
concurrency:
|
||||
group: deploy-master
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Build and FTP Sync
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install PlatformIO
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install platformio
|
||||
|
||||
- name: Build firmware bundle
|
||||
run: pio run -e uno_linux
|
||||
|
||||
- name: Install lftp client
|
||||
run: |
|
||||
if command -v lftp >/dev/null 2>&1; then
|
||||
echo 'lftp is already available on this runner image'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if command -v sudo >/dev/null 2>&1; then
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y lftp
|
||||
exit 0
|
||||
fi
|
||||
|
||||
apt-get update
|
||||
apt-get install -y lftp
|
||||
|
||||
- name: Upload firmware bundle via lftp
|
||||
env:
|
||||
FTP_SERVER: ${{ secrets.FTP_SERVER }}
|
||||
FTP_USERNAME: ${{ secrets.FTP_USERNAME }}
|
||||
FTP_PASSWORD: ${{ secrets.FTP_PASSWORD }}
|
||||
FTP_SERVER_DIR: ${{ secrets.FTP_SERVER_DIR }}
|
||||
run: |
|
||||
lftp <<EOF
|
||||
set cmd:fail-exit true
|
||||
set net:timeout 120
|
||||
set net:max-retries 2
|
||||
set net:reconnect-interval-base 5
|
||||
set ftp:passive-mode true
|
||||
set ftp:prefer-epsv false
|
||||
set ftp:ssl-force true
|
||||
set ftp:ssl-protect-data true
|
||||
set ftp:ssl-auth TLS
|
||||
set ssl:verify-certificate no
|
||||
open -u "$FTP_USERNAME","$FTP_PASSWORD" -p 21 "$FTP_SERVER"
|
||||
mirror -R --verbose --delete \
|
||||
--exclude-glob .DS_Store \
|
||||
firmware/ "$FTP_SERVER_DIR"
|
||||
quit
|
||||
EOF
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -15,6 +15,8 @@ local.properties
|
||||
.classpath
|
||||
.settings/
|
||||
.loadpath
|
||||
.vscode
|
||||
.pio
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
@@ -213,3 +215,5 @@ pip-log.txt
|
||||
|
||||
#Mr Developer
|
||||
.mr.developer.cfg
|
||||
src/credentials.h
|
||||
platformio_override.ini
|
||||
|
||||
63
AGENTS.md
Normal file
63
AGENTS.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# AGENTS.md
|
||||
|
||||
## Purpose
|
||||
This repository contains EggDuino firmware (EggBot protocol emulation) for an ESP32 build target using PlatformIO.
|
||||
Use this file as the working contract for code agents in this repo.
|
||||
|
||||
## Repository Map
|
||||
- `src/main.cpp`: firmware entrypoint (`setup`/`loop`), serial command processing, optional button/test toggles.
|
||||
- `src/Functions.cpp`: EggBot command handlers (`SM`, `SP`, `EM`, `SC`, `QP`, etc.) and command registration.
|
||||
- `src/Helper_Functions.cpp`: hardware init, motor/servo control, move preparation, ACK/error responses.
|
||||
- `src/Config_Web.cpp`: optional Wi-Fi web UI and JSON config persistence via SPIFFS.
|
||||
- `src/Logging.cpp`: ring-buffer log collection and `/api/logs` JSON serialization.
|
||||
- `include/EggDuino.h`: global declarations, pin/microstep constants, cross-file API.
|
||||
- `lib/SerialCommand/src/*`: local serial command parser implementation.
|
||||
- `platformio.ini`: single build environment (`[env:uno]`) that actually targets `esp32dev`.
|
||||
|
||||
## Build, Flash, Monitor
|
||||
- Build: `~/.platformio/penv/bin/pio run -e uno`
|
||||
- Upload: `~/.platformio/penv/bin/pio run -e uno -t upload`
|
||||
- Serial monitor: `~/.platformio/penv/bin/pio device monitor -b 115200`
|
||||
|
||||
Notes:
|
||||
- `platformio.ini` currently pins `upload_port = /dev/tty.usbserial-110`; treat this as machine-local and change only when required.
|
||||
- There are no unit tests in this repository; a successful PlatformIO build is the minimum validation.
|
||||
|
||||
## Local Config And Secrets
|
||||
- Wi-Fi config is provided by `src/credentials.h`.
|
||||
- Keep `src/credentials.h` local-only; `.gitignore` already excludes it.
|
||||
- For default behavior (no Wi-Fi web server), keep:
|
||||
- `const char *kWifiSsid = 0;`
|
||||
- `const char *kWifiPassword = 0;`
|
||||
|
||||
## Agent Rules For Code Changes
|
||||
1. Preserve EggBot serial protocol behavior.
|
||||
- Commands are wired in `makeComInterface()`.
|
||||
- ACK/error responses are protocol-significant (`"OK\\r\\n"` and `"unknown CMD\\r\\n"`).
|
||||
2. Keep movement math semantics intact.
|
||||
- `prepareMove()` handles microstep correction and step error accumulation (`g_iRotStepError`, `g_iPenStepError`).
|
||||
- Do not replace this with naive float-heavy logic unless explicitly requested.
|
||||
3. Respect real-time loop constraints.
|
||||
- `loop()` must remain non-blocking except for protocol-required waits.
|
||||
- Avoid adding expensive dynamic allocation in high-frequency paths.
|
||||
4. Keep hardware mapping centralized.
|
||||
- Pin and microstep constants belong in `include/EggDuino.h`.
|
||||
- If changing pinouts, update only there and keep ESP32/non-ESP32 guards coherent.
|
||||
5. Do not hand-edit generated IDE metadata.
|
||||
- `.vscode/c_cpp_properties.json` and `.vscode/launch.json` are generated by PlatformIO.
|
||||
- Prefer edits in `platformio.ini` instead.
|
||||
6. Keep edits minimal and targeted.
|
||||
- Match existing style (global `g_*` variables, Arduino types, simple free functions).
|
||||
- Avoid broad refactors unless explicitly requested.
|
||||
|
||||
## When Adding Config Parameters To Web UI
|
||||
`Config_Web.cpp` currently has an empty `configParameters[]` array. To add persisted parameters:
|
||||
1. Add the backing global variable (typically in existing global state).
|
||||
2. Add a `ConfigParameter` entry to `configParameters[]` with key, pointer, description, default.
|
||||
3. Ensure defaults are safe in `applyDefaults()` flow.
|
||||
4. Rebuild and verify `GET/POST /api/config` still round-trips.
|
||||
|
||||
## Definition Of Done For Agents
|
||||
Before finishing, run:
|
||||
1. `~/.platformio/penv/bin/pio run -e uno`
|
||||
2. Summarize changed files and any behavior-impacting protocol/hardware implications.
|
||||
633
AccelStepper.cpp
633
AccelStepper.cpp
@@ -1,633 +0,0 @@
|
||||
// AccelStepper.cpp
|
||||
//
|
||||
// Copyright (C) 2009-2013 Mike McCauley
|
||||
// $Id: AccelStepper.cpp,v 1.19 2014/10/31 06:05:27 mikem Exp mikem $
|
||||
|
||||
#include "AccelStepper.h"
|
||||
|
||||
#if 0
|
||||
// Some debugging assistance
|
||||
void dump(uint8_t* p, int l)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < l; i++)
|
||||
{
|
||||
Serial.print(p[i], HEX);
|
||||
Serial.print(" ");
|
||||
}
|
||||
Serial.println("");
|
||||
}
|
||||
#endif
|
||||
|
||||
void AccelStepper::moveTo(long absolute)
|
||||
{
|
||||
if (_targetPos != absolute)
|
||||
{
|
||||
_targetPos = absolute;
|
||||
computeNewSpeed();
|
||||
// compute new n?
|
||||
}
|
||||
}
|
||||
|
||||
void AccelStepper::move(long relative)
|
||||
{
|
||||
moveTo(_currentPos + relative);
|
||||
}
|
||||
|
||||
// Implements steps according to the current step interval
|
||||
// You must call this at least once per step
|
||||
// returns true if a step occurred
|
||||
boolean AccelStepper::runSpeed()
|
||||
{
|
||||
// Dont do anything unless we actually have a step interval
|
||||
if (!_stepInterval)
|
||||
return false;
|
||||
|
||||
unsigned long time = micros();
|
||||
unsigned long nextStepTime = _lastStepTime + _stepInterval;
|
||||
// Gymnastics to detect wrapping of either the nextStepTime and/or the current time
|
||||
if ( ((nextStepTime >= _lastStepTime) && ((time >= nextStepTime) || (time < _lastStepTime)))
|
||||
|| ((nextStepTime < _lastStepTime) && ((time >= nextStepTime) && (time < _lastStepTime))))
|
||||
{
|
||||
if (_direction == DIRECTION_CW)
|
||||
{
|
||||
// Clockwise
|
||||
_currentPos += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Anticlockwise
|
||||
_currentPos -= 1;
|
||||
}
|
||||
step(_currentPos);
|
||||
|
||||
_lastStepTime = time;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
long AccelStepper::distanceToGo()
|
||||
{
|
||||
return _targetPos - _currentPos;
|
||||
}
|
||||
|
||||
long AccelStepper::targetPosition()
|
||||
{
|
||||
return _targetPos;
|
||||
}
|
||||
|
||||
long AccelStepper::currentPosition()
|
||||
{
|
||||
return _currentPos;
|
||||
}
|
||||
|
||||
// Useful during initialisations or after initial positioning
|
||||
// Sets speed to 0
|
||||
void AccelStepper::setCurrentPosition(long position)
|
||||
{
|
||||
_targetPos = _currentPos = position;
|
||||
_n = 0;
|
||||
_stepInterval = 0;
|
||||
}
|
||||
|
||||
void AccelStepper::computeNewSpeed()
|
||||
{
|
||||
long distanceTo = distanceToGo(); // +ve is clockwise from curent location
|
||||
|
||||
long stepsToStop = (long)((_speed * _speed) / (2.0 * _acceleration)); // Equation 16
|
||||
|
||||
if (distanceTo == 0 && stepsToStop <= 1)
|
||||
{
|
||||
// We are at the target and its time to stop
|
||||
_stepInterval = 0;
|
||||
_speed = 0.0;
|
||||
_n = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (distanceTo > 0)
|
||||
{
|
||||
// We are anticlockwise from the target
|
||||
// Need to go clockwise from here, maybe decelerate now
|
||||
if (_n > 0)
|
||||
{
|
||||
// Currently accelerating, need to decel now? Or maybe going the wrong way?
|
||||
if ((stepsToStop >= distanceTo) || _direction == DIRECTION_CCW)
|
||||
_n = -stepsToStop; // Start deceleration
|
||||
}
|
||||
else if (_n < 0)
|
||||
{
|
||||
// Currently decelerating, need to accel again?
|
||||
if ((stepsToStop < distanceTo) && _direction == DIRECTION_CW)
|
||||
_n = -_n; // Start accceleration
|
||||
}
|
||||
}
|
||||
else if (distanceTo < 0)
|
||||
{
|
||||
// We are clockwise from the target
|
||||
// Need to go anticlockwise from here, maybe decelerate
|
||||
if (_n > 0)
|
||||
{
|
||||
// Currently accelerating, need to decel now? Or maybe going the wrong way?
|
||||
if ((stepsToStop >= -distanceTo) || _direction == DIRECTION_CW)
|
||||
_n = -stepsToStop; // Start deceleration
|
||||
}
|
||||
else if (_n < 0)
|
||||
{
|
||||
// Currently decelerating, need to accel again?
|
||||
if ((stepsToStop < -distanceTo) && _direction == DIRECTION_CCW)
|
||||
_n = -_n; // Start accceleration
|
||||
}
|
||||
}
|
||||
|
||||
// Need to accelerate or decelerate
|
||||
if (_n == 0)
|
||||
{
|
||||
// First step from stopped
|
||||
_cn = _c0;
|
||||
_direction = (distanceTo > 0) ? DIRECTION_CW : DIRECTION_CCW;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subsequent step. Works for accel (n is +_ve) and decel (n is -ve).
|
||||
_cn = _cn - ((2.0 * _cn) / ((4.0 * _n) + 1)); // Equation 13
|
||||
_cn = max(_cn, _cmin);
|
||||
}
|
||||
_n++;
|
||||
_stepInterval = _cn;
|
||||
_speed = 1000000.0 / _cn;
|
||||
if (_direction == DIRECTION_CCW)
|
||||
_speed = -_speed;
|
||||
|
||||
#if 0
|
||||
Serial.println(_speed);
|
||||
Serial.println(_acceleration);
|
||||
Serial.println(_cn);
|
||||
Serial.println(_c0);
|
||||
Serial.println(_n);
|
||||
Serial.println(_stepInterval);
|
||||
Serial.println(distanceTo);
|
||||
Serial.println(stepsToStop);
|
||||
Serial.println("-----");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Run the motor to implement speed and acceleration in order to proceed to the target position
|
||||
// You must call this at least once per step, preferably in your main loop
|
||||
// If the motor is in the desired position, the cost is very small
|
||||
// returns true if the motor is still running to the target position.
|
||||
boolean AccelStepper::run()
|
||||
{
|
||||
if (runSpeed())
|
||||
computeNewSpeed();
|
||||
return _speed != 0.0 || distanceToGo() != 0;
|
||||
}
|
||||
|
||||
AccelStepper::AccelStepper(uint8_t interface, uint8_t pin1, uint8_t pin2, uint8_t pin3, uint8_t pin4, bool enable)
|
||||
{
|
||||
_interface = interface;
|
||||
_currentPos = 0;
|
||||
_targetPos = 0;
|
||||
_speed = 0.0;
|
||||
_maxSpeed = 1.0;
|
||||
_acceleration = 0.0;
|
||||
_sqrt_twoa = 1.0;
|
||||
_stepInterval = 0;
|
||||
_minPulseWidth = 1;
|
||||
_enablePin = 0xff;
|
||||
_lastStepTime = 0;
|
||||
_pin[0] = pin1;
|
||||
_pin[1] = pin2;
|
||||
_pin[2] = pin3;
|
||||
_pin[3] = pin4;
|
||||
|
||||
// NEW
|
||||
_n = 0;
|
||||
_c0 = 0.0;
|
||||
_cn = 0.0;
|
||||
_cmin = 1.0;
|
||||
_direction = DIRECTION_CCW;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 4; i++)
|
||||
_pinInverted[i] = 0;
|
||||
if (enable)
|
||||
enableOutputs();
|
||||
// Some reasonable default
|
||||
setAcceleration(1);
|
||||
}
|
||||
|
||||
AccelStepper::AccelStepper(void (*forward)(), void (*backward)())
|
||||
{
|
||||
_interface = 0;
|
||||
_currentPos = 0;
|
||||
_targetPos = 0;
|
||||
_speed = 0.0;
|
||||
_maxSpeed = 1.0;
|
||||
_acceleration = 0.0;
|
||||
_sqrt_twoa = 1.0;
|
||||
_stepInterval = 0;
|
||||
_minPulseWidth = 1;
|
||||
_enablePin = 0xff;
|
||||
_lastStepTime = 0;
|
||||
_pin[0] = 0;
|
||||
_pin[1] = 0;
|
||||
_pin[2] = 0;
|
||||
_pin[3] = 0;
|
||||
_forward = forward;
|
||||
_backward = backward;
|
||||
|
||||
// NEW
|
||||
_n = 0;
|
||||
_c0 = 0.0;
|
||||
_cn = 0.0;
|
||||
_cmin = 1.0;
|
||||
_direction = DIRECTION_CCW;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 4; i++)
|
||||
_pinInverted[i] = 0;
|
||||
// Some reasonable default
|
||||
setAcceleration(1);
|
||||
}
|
||||
|
||||
void AccelStepper::setMaxSpeed(float speed)
|
||||
{
|
||||
if (_maxSpeed != speed)
|
||||
{
|
||||
_maxSpeed = speed;
|
||||
_cmin = 1000000.0 / speed;
|
||||
// Recompute _n from current speed and adjust speed if accelerating or cruising
|
||||
if (_n > 0)
|
||||
{
|
||||
_n = (long)((_speed * _speed) / (2.0 * _acceleration)); // Equation 16
|
||||
computeNewSpeed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AccelStepper::setAcceleration(float acceleration)
|
||||
{
|
||||
if (acceleration == 0.0)
|
||||
return;
|
||||
if (_acceleration != acceleration)
|
||||
{
|
||||
// Recompute _n per Equation 17
|
||||
_n = _n * (_acceleration / acceleration);
|
||||
// New c0 per Equation 7, with correction per Equation 15
|
||||
_c0 = 0.676 * sqrt(2.0 / acceleration) * 1000000.0; // Equation 15
|
||||
_acceleration = acceleration;
|
||||
computeNewSpeed();
|
||||
}
|
||||
}
|
||||
|
||||
void AccelStepper::setSpeed(float speed)
|
||||
{
|
||||
if (speed == _speed)
|
||||
return;
|
||||
speed = constrain(speed, -_maxSpeed, _maxSpeed);
|
||||
if (speed == 0.0)
|
||||
_stepInterval = 0;
|
||||
else
|
||||
{
|
||||
_stepInterval = fabs(1000000.0 / speed);
|
||||
_direction = (speed > 0.0) ? DIRECTION_CW : DIRECTION_CCW;
|
||||
}
|
||||
_speed = speed;
|
||||
}
|
||||
|
||||
float AccelStepper::speed()
|
||||
{
|
||||
return _speed;
|
||||
}
|
||||
|
||||
// Subclasses can override
|
||||
void AccelStepper::step(long step)
|
||||
{
|
||||
switch (_interface)
|
||||
{
|
||||
case FUNCTION:
|
||||
step0(step);
|
||||
break;
|
||||
|
||||
case DRIVER:
|
||||
step1(step);
|
||||
break;
|
||||
|
||||
case FULL2WIRE:
|
||||
step2(step);
|
||||
break;
|
||||
|
||||
case FULL3WIRE:
|
||||
step3(step);
|
||||
break;
|
||||
|
||||
case FULL4WIRE:
|
||||
step4(step);
|
||||
break;
|
||||
|
||||
case HALF3WIRE:
|
||||
step6(step);
|
||||
break;
|
||||
|
||||
case HALF4WIRE:
|
||||
step8(step);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// You might want to override this to implement eg serial output
|
||||
// bit 0 of the mask corresponds to _pin[0]
|
||||
// bit 1 of the mask corresponds to _pin[1]
|
||||
// ....
|
||||
void AccelStepper::setOutputPins(uint8_t mask)
|
||||
{
|
||||
uint8_t numpins = 2;
|
||||
if (_interface == FULL4WIRE || _interface == HALF4WIRE)
|
||||
numpins = 4;
|
||||
else if (_interface == FULL3WIRE || _interface == HALF3WIRE)
|
||||
numpins = 3;
|
||||
uint8_t i;
|
||||
for (i = 0; i < numpins; i++)
|
||||
digitalWrite(_pin[i], (mask & (1 << i)) ? (HIGH ^ _pinInverted[i]) : (LOW ^ _pinInverted[i]));
|
||||
}
|
||||
|
||||
// 0 pin step function (ie for functional usage)
|
||||
void AccelStepper::step0(long step)
|
||||
{
|
||||
if (_speed > 0)
|
||||
_forward();
|
||||
else
|
||||
_backward();
|
||||
}
|
||||
|
||||
// 1 pin step function (ie for stepper drivers)
|
||||
// This is passed the current step number (0 to 7)
|
||||
// Subclasses can override
|
||||
void AccelStepper::step1(long step)
|
||||
{
|
||||
// _pin[0] is step, _pin[1] is direction
|
||||
setOutputPins(_direction ? 0b10 : 0b00); // Set direction first else get rogue pulses
|
||||
setOutputPins(_direction ? 0b11 : 0b01); // step HIGH
|
||||
// Caution 200ns setup time
|
||||
// Delay the minimum allowed pulse width
|
||||
delayMicroseconds(_minPulseWidth);
|
||||
setOutputPins(_direction ? 0b10 : 0b00); // step LOW
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 2 pin step function
|
||||
// This is passed the current step number (0 to 7)
|
||||
// Subclasses can override
|
||||
void AccelStepper::step2(long step)
|
||||
{
|
||||
switch (step & 0x3)
|
||||
{
|
||||
case 0: /* 01 */
|
||||
setOutputPins(0b10);
|
||||
break;
|
||||
|
||||
case 1: /* 11 */
|
||||
setOutputPins(0b11);
|
||||
break;
|
||||
|
||||
case 2: /* 10 */
|
||||
setOutputPins(0b01);
|
||||
break;
|
||||
|
||||
case 3: /* 00 */
|
||||
setOutputPins(0b00);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 3 pin step function
|
||||
// This is passed the current step number (0 to 7)
|
||||
// Subclasses can override
|
||||
void AccelStepper::step3(long step)
|
||||
{
|
||||
switch (step % 3)
|
||||
{
|
||||
case 0: // 100
|
||||
setOutputPins(0b100);
|
||||
break;
|
||||
|
||||
case 1: // 001
|
||||
setOutputPins(0b001);
|
||||
break;
|
||||
|
||||
case 2: //010
|
||||
setOutputPins(0b010);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 4 pin step function for half stepper
|
||||
// This is passed the current step number (0 to 7)
|
||||
// Subclasses can override
|
||||
void AccelStepper::step4(long step)
|
||||
{
|
||||
switch (step & 0x3)
|
||||
{
|
||||
case 0: // 1010
|
||||
setOutputPins(0b0101);
|
||||
break;
|
||||
|
||||
case 1: // 0110
|
||||
setOutputPins(0b0110);
|
||||
break;
|
||||
|
||||
case 2: //0101
|
||||
setOutputPins(0b1010);
|
||||
break;
|
||||
|
||||
case 3: //1001
|
||||
setOutputPins(0b1001);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 3 pin half step function
|
||||
// This is passed the current step number (0 to 7)
|
||||
// Subclasses can override
|
||||
void AccelStepper::step6(long step)
|
||||
{
|
||||
switch (step % 6)
|
||||
{
|
||||
case 0: // 100
|
||||
setOutputPins(0b100);
|
||||
break;
|
||||
|
||||
case 1: // 101
|
||||
setOutputPins(0b101);
|
||||
break;
|
||||
|
||||
case 2: // 001
|
||||
setOutputPins(0b001);
|
||||
break;
|
||||
|
||||
case 3: // 011
|
||||
setOutputPins(0b011);
|
||||
break;
|
||||
|
||||
case 4: // 010
|
||||
setOutputPins(0b010);
|
||||
break;
|
||||
|
||||
case 5: // 011
|
||||
setOutputPins(0b110);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 4 pin half step function
|
||||
// This is passed the current step number (0 to 7)
|
||||
// Subclasses can override
|
||||
void AccelStepper::step8(long step)
|
||||
{
|
||||
switch (step & 0x7)
|
||||
{
|
||||
case 0: // 1000
|
||||
setOutputPins(0b0001);
|
||||
break;
|
||||
|
||||
case 1: // 1010
|
||||
setOutputPins(0b0101);
|
||||
break;
|
||||
|
||||
case 2: // 0010
|
||||
setOutputPins(0b0100);
|
||||
break;
|
||||
|
||||
case 3: // 0110
|
||||
setOutputPins(0b0110);
|
||||
break;
|
||||
|
||||
case 4: // 0100
|
||||
setOutputPins(0b0010);
|
||||
break;
|
||||
|
||||
case 5: //0101
|
||||
setOutputPins(0b1010);
|
||||
break;
|
||||
|
||||
case 6: // 0001
|
||||
setOutputPins(0b1000);
|
||||
break;
|
||||
|
||||
case 7: //1001
|
||||
setOutputPins(0b1001);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prevents power consumption on the outputs
|
||||
void AccelStepper::disableOutputs()
|
||||
{
|
||||
if (! _interface) return;
|
||||
|
||||
setOutputPins(0); // Handles inversion automatically
|
||||
if (_enablePin != 0xff)
|
||||
digitalWrite(_enablePin, LOW ^ _enableInverted);
|
||||
}
|
||||
|
||||
void AccelStepper::enableOutputs()
|
||||
{
|
||||
if (! _interface)
|
||||
return;
|
||||
|
||||
pinMode(_pin[0], OUTPUT);
|
||||
pinMode(_pin[1], OUTPUT);
|
||||
if (_interface == FULL4WIRE || _interface == HALF4WIRE)
|
||||
{
|
||||
pinMode(_pin[2], OUTPUT);
|
||||
pinMode(_pin[3], OUTPUT);
|
||||
}
|
||||
else if (_interface == FULL3WIRE || _interface == HALF3WIRE)
|
||||
{
|
||||
pinMode(_pin[2], OUTPUT);
|
||||
}
|
||||
|
||||
if (_enablePin != 0xff)
|
||||
{
|
||||
pinMode(_enablePin, OUTPUT);
|
||||
digitalWrite(_enablePin, HIGH ^ _enableInverted);
|
||||
}
|
||||
}
|
||||
|
||||
void AccelStepper::setMinPulseWidth(unsigned int minWidth)
|
||||
{
|
||||
_minPulseWidth = minWidth;
|
||||
}
|
||||
|
||||
void AccelStepper::setEnablePin(uint8_t enablePin)
|
||||
{
|
||||
_enablePin = enablePin;
|
||||
|
||||
// This happens after construction, so init pin now.
|
||||
if (_enablePin != 0xff)
|
||||
{
|
||||
pinMode(_enablePin, OUTPUT);
|
||||
digitalWrite(_enablePin, HIGH ^ _enableInverted);
|
||||
}
|
||||
}
|
||||
|
||||
void AccelStepper::setPinsInverted(bool directionInvert, bool stepInvert, bool enableInvert)
|
||||
{
|
||||
_pinInverted[0] = stepInvert;
|
||||
_pinInverted[1] = directionInvert;
|
||||
_enableInverted = enableInvert;
|
||||
}
|
||||
|
||||
void AccelStepper::setPinsInverted(bool pin1Invert, bool pin2Invert, bool pin3Invert, bool pin4Invert, bool enableInvert)
|
||||
{
|
||||
_pinInverted[0] = pin1Invert;
|
||||
_pinInverted[1] = pin2Invert;
|
||||
_pinInverted[2] = pin3Invert;
|
||||
_pinInverted[3] = pin4Invert;
|
||||
_enableInverted = enableInvert;
|
||||
}
|
||||
|
||||
// Blocks until the target position is reached and stopped
|
||||
void AccelStepper::runToPosition()
|
||||
{
|
||||
while (run())
|
||||
;
|
||||
}
|
||||
|
||||
boolean AccelStepper::runSpeedToPosition()
|
||||
{
|
||||
if (_targetPos == _currentPos)
|
||||
return false;
|
||||
if (_targetPos >_currentPos)
|
||||
_direction = DIRECTION_CW;
|
||||
else
|
||||
_direction = DIRECTION_CCW;
|
||||
return runSpeed();
|
||||
}
|
||||
|
||||
// Blocks until the new target position is reached
|
||||
void AccelStepper::runToNewPosition(long position)
|
||||
{
|
||||
moveTo(position);
|
||||
runToPosition();
|
||||
}
|
||||
|
||||
void AccelStepper::stop()
|
||||
{
|
||||
if (_speed != 0.0)
|
||||
{
|
||||
long stepsToStop = (long)((_speed * _speed) / (2.0 * _acceleration)) + 1; // Equation 16 (+integer rounding)
|
||||
if (_speed > 0)
|
||||
move(stepsToStop);
|
||||
else
|
||||
move(-stepsToStop);
|
||||
}
|
||||
}
|
||||
666
AccelStepper.h
666
AccelStepper.h
@@ -1,666 +0,0 @@
|
||||
// AccelStepper.h
|
||||
//
|
||||
/// \mainpage AccelStepper library for Arduino
|
||||
///
|
||||
/// This is the Arduino AccelStepper library.
|
||||
/// It provides an object-oriented interface for 2, 3 or 4 pin stepper motors.
|
||||
///
|
||||
/// The standard Arduino IDE includes the Stepper library
|
||||
/// (http://arduino.cc/en/Reference/Stepper) for stepper motors. It is
|
||||
/// perfectly adequate for simple, single motor applications.
|
||||
///
|
||||
/// AccelStepper significantly improves on the standard Arduino Stepper library in several ways:
|
||||
/// \li Supports acceleration and deceleration
|
||||
/// \li Supports multiple simultaneous steppers, with independent concurrent stepping on each stepper
|
||||
/// \li API functions never delay() or block
|
||||
/// \li Supports 2, 3 and 4 wire steppers, plus 3 and 4 wire half steppers.
|
||||
/// \li Supports alternate stepping functions to enable support of AFMotor (https://github.com/adafruit/Adafruit-Motor-Shield-library)
|
||||
/// \li Supports stepper drivers such as the Sparkfun EasyDriver (based on 3967 driver chip)
|
||||
/// \li Very slow speeds are supported
|
||||
/// \li Extensive API
|
||||
/// \li Subclass support
|
||||
///
|
||||
/// The latest version of this documentation can be downloaded from
|
||||
/// http://www.airspayce.com/mikem/arduino/AccelStepper
|
||||
/// The version of the package that this documentation refers to can be downloaded
|
||||
/// from http://www.airspayce.com/mikem/arduino/AccelStepper/AccelStepper-1.47.zip
|
||||
///
|
||||
/// Example Arduino programs are included to show the main modes of use.
|
||||
///
|
||||
/// You can also find online help and discussion at http://groups.google.com/group/accelstepper
|
||||
/// Please use that group for all questions and discussions on this topic.
|
||||
/// Do not contact the author directly, unless it is to discuss commercial licensing.
|
||||
/// Before asking a question or reporting a bug, please read http://www.catb.org/esr/faqs/smart-questions.html
|
||||
///
|
||||
/// Tested on Arduino Diecimila and Mega with arduino-0018 & arduino-0021
|
||||
/// on OpenSuSE 11.1 and avr-libc-1.6.1-1.15,
|
||||
/// cross-avr-binutils-2.19-9.1, cross-avr-gcc-4.1.3_20080612-26.5.
|
||||
/// Tested on Teensy http://www.pjrc.com/teensy including Teensy 3.1 built using Arduino IDE 1.0.5 with
|
||||
/// teensyduino addon 1.18 and later.
|
||||
///
|
||||
/// \par Installation
|
||||
///
|
||||
/// Install in the usual way: unzip the distribution zip file to the libraries
|
||||
/// sub-folder of your sketchbook.
|
||||
///
|
||||
/// \par Theory
|
||||
///
|
||||
/// This code uses speed calculations as described in
|
||||
/// "Generate stepper-motor speed profiles in real time" by David Austin
|
||||
/// http://fab.cba.mit.edu/classes/MIT/961.09/projects/i0/Stepper_Motor_Speed_Profile.pdf
|
||||
/// with the exception that AccelStepper uses steps per second rather than radians per second
|
||||
/// (because we dont know the step angle of the motor)
|
||||
/// An initial step interval is calculated for the first step, based on the desired acceleration
|
||||
/// On subsequent steps, shorter step intervals are calculated based
|
||||
/// on the previous step until max speed is achieved.
|
||||
///
|
||||
/// \par Donations
|
||||
///
|
||||
/// This library is offered under a free GPL license for those who want to use it that way.
|
||||
/// We try hard to keep it up to date, fix bugs
|
||||
/// and to provide free support. If this library has helped you save time or money, please consider donating at
|
||||
/// http://www.airspayce.com or here:
|
||||
///
|
||||
/// \htmlonly <form action="https://www.paypal.com/cgi-bin/webscr" method="post"><input type="hidden" name="cmd" value="_donations" /> <input type="hidden" name="business" value="mikem@airspayce.com" /> <input type="hidden" name="lc" value="AU" /> <input type="hidden" name="item_name" value="Airspayce" /> <input type="hidden" name="item_number" value="AccelStepper" /> <input type="hidden" name="currency_code" value="USD" /> <input type="hidden" name="bn" value="PP-DonationsBF:btn_donateCC_LG.gif:NonHosted" /> <input type="image" alt="PayPal — The safer, easier way to pay online." name="submit" src="https://www.paypalobjects.com/en_AU/i/btn/btn_donateCC_LG.gif" /> <img alt="" src="https://www.paypalobjects.com/en_AU/i/scr/pixel.gif" width="1" height="1" border="0" /></form> \endhtmlonly
|
||||
///
|
||||
/// \par Trademarks
|
||||
///
|
||||
/// AccelStepper is a trademark of AirSpayce Pty Ltd. The AccelStepper mark was first used on April 26 2010 for
|
||||
/// international trade, and is used only in relation to motor control hardware and software.
|
||||
/// It is not to be confused with any other similar marks covering other goods and services.
|
||||
///
|
||||
/// \par Copyright
|
||||
///
|
||||
/// This software is Copyright (C) 2010 Mike McCauley. Use is subject to license
|
||||
/// conditions. The main licensing options available are GPL V2 or Commercial:
|
||||
///
|
||||
/// \par Open Source Licensing GPL V2
|
||||
/// This is the appropriate option if you want to share the source code of your
|
||||
/// application with everyone you distribute it to, and you also want to give them
|
||||
/// the right to share who uses it. If you wish to use this software under Open
|
||||
/// Source Licensing, you must contribute all your source code to the open source
|
||||
/// community in accordance with the GPL Version 2 when your application is
|
||||
/// distributed. See http://www.gnu.org/copyleft/gpl.html
|
||||
///
|
||||
/// \par Commercial Licensing
|
||||
/// This is the appropriate option if you are creating proprietary applications
|
||||
/// and you are not prepared to distribute and share the source code of your
|
||||
/// application. Contact info@airspayce.com for details.
|
||||
///
|
||||
/// \par Revision History
|
||||
/// \version 1.0 Initial release
|
||||
///
|
||||
/// \version 1.1 Added speed() function to get the current speed.
|
||||
/// \version 1.2 Added runSpeedToPosition() submitted by Gunnar Arndt.
|
||||
/// \version 1.3 Added support for stepper drivers (ie with Step and Direction inputs) with _pins == 1
|
||||
/// \version 1.4 Added functional contructor to support AFMotor, contributed by Limor, with example sketches.
|
||||
/// \version 1.5 Improvements contributed by Peter Mousley: Use of microsecond steps and other speed improvements
|
||||
/// to increase max stepping speed to about 4kHz. New option for user to set the min allowed pulse width.
|
||||
/// Added checks for already running at max speed and skip further calcs if so.
|
||||
/// \version 1.6 Fixed a problem with wrapping of microsecond stepping that could cause stepping to hang.
|
||||
/// Reported by Sandy Noble.
|
||||
/// Removed redundant _lastRunTime member.
|
||||
/// \version 1.7 Fixed a bug where setCurrentPosition() did not always work as expected.
|
||||
/// Reported by Peter Linhart.
|
||||
/// \version 1.8 Added support for 4 pin half-steppers, requested by Harvey Moon
|
||||
/// \version 1.9 setCurrentPosition() now also sets motor speed to 0.
|
||||
/// \version 1.10 Builds on Arduino 1.0
|
||||
/// \version 1.11 Improvments from Michael Ellison:
|
||||
/// Added optional enable line support for stepper drivers
|
||||
/// Added inversion for step/direction/enable lines for stepper drivers
|
||||
/// \version 1.12 Announce Google Group
|
||||
/// \version 1.13 Improvements to speed calculation. Cost of calculation is now less in the worst case,
|
||||
/// and more or less constant in all cases. This should result in slightly beter high speed performance, and
|
||||
/// reduce anomalous speed glitches when other steppers are accelerating.
|
||||
/// However, its hard to see how to replace the sqrt() required at the very first step from 0 speed.
|
||||
/// \version 1.14 Fixed a problem with compiling under arduino 0021 reported by EmbeddedMan
|
||||
/// \version 1.15 Fixed a problem with runSpeedToPosition which did not correctly handle
|
||||
/// running backwards to a smaller target position. Added examples
|
||||
/// \version 1.16 Fixed some cases in the code where abs() was used instead of fabs().
|
||||
/// \version 1.17 Added example ProportionalControl
|
||||
/// \version 1.18 Fixed a problem: If one calls the funcion runSpeed() when Speed is zero, it makes steps
|
||||
/// without counting. reported by Friedrich, Klappenbach.
|
||||
/// \version 1.19 Added MotorInterfaceType and symbolic names for the number of pins to use
|
||||
/// for the motor interface. Updated examples to suit.
|
||||
/// Replaced individual pin assignment variables _pin1, _pin2 etc with array _pin[4].
|
||||
/// _pins member changed to _interface.
|
||||
/// Added _pinInverted array to simplify pin inversion operations.
|
||||
/// Added new function setOutputPins() which sets the motor output pins.
|
||||
/// It can be overridden in order to provide, say, serial output instead of parallel output
|
||||
/// Some refactoring and code size reduction.
|
||||
/// \version 1.20 Improved documentation and examples to show need for correctly
|
||||
/// specifying AccelStepper::FULL4WIRE and friends.
|
||||
/// \version 1.21 Fixed a problem where desiredSpeed could compute the wrong step acceleration
|
||||
/// when _speed was small but non-zero. Reported by Brian Schmalz.
|
||||
/// Precompute sqrt_twoa to improve performance and max possible stepping speed
|
||||
/// \version 1.22 Added Bounce.pde example
|
||||
/// Fixed a problem where calling moveTo(), setMaxSpeed(), setAcceleration() more
|
||||
/// frequently than the step time, even
|
||||
/// with the same values, would interfere with speed calcs. Now a new speed is computed
|
||||
/// only if there was a change in the set value. Reported by Brian Schmalz.
|
||||
/// \version 1.23 Rewrite of the speed algorithms in line with
|
||||
/// http://fab.cba.mit.edu/classes/MIT/961.09/projects/i0/Stepper_Motor_Speed_Profile.pdf
|
||||
/// Now expect smoother and more linear accelerations and decelerations. The desiredSpeed()
|
||||
/// function was removed.
|
||||
/// \version 1.24 Fixed a problem introduced in 1.23: with runToPosition, which did never returned
|
||||
/// \version 1.25 Now ignore attempts to set acceleration to 0.0
|
||||
/// \version 1.26 Fixed a problem where certina combinations of speed and accelration could cause
|
||||
/// oscillation about the target position.
|
||||
/// \version 1.27 Added stop() function to stop as fast as possible with current acceleration parameters.
|
||||
/// Also added new Quickstop example showing its use.
|
||||
/// \version 1.28 Fixed another problem where certain combinations of speed and accelration could cause
|
||||
/// oscillation about the target position.
|
||||
/// Added support for 3 wire full and half steppers such as Hard Disk Drive spindle.
|
||||
/// Contributed by Yuri Ivatchkovitch.
|
||||
/// \version 1.29 Fixed a problem that could cause a DRIVER stepper to continually step
|
||||
/// with some sketches. Reported by Vadim.
|
||||
/// \version 1.30 Fixed a problem that could cause stepper to back up a few steps at the end of
|
||||
/// accelerated travel with certain speeds. Reported and patched by jolo.
|
||||
/// \version 1.31 Updated author and distribution location details to airspayce.com
|
||||
/// \version 1.32 Fixed a problem with enableOutputs() and setEnablePin on Arduino Due that
|
||||
/// prevented the enable pin changing stae correctly. Reported by Duane Bishop.
|
||||
/// \version 1.33 Fixed an error in example AFMotor_ConstantSpeed.pde did not setMaxSpeed();
|
||||
/// Fixed a problem that caused incorrect pin sequencing of FULL3WIRE and HALF3WIRE.
|
||||
/// Unfortunately this meant changing the signature for all step*() functions.
|
||||
/// Added example MotorShield, showing how to use AdaFruit Motor Shield to control
|
||||
/// a 3 phase motor such as a HDD spindle motor (and without using the AFMotor library.
|
||||
/// \version 1.34 Added setPinsInverted(bool pin1Invert, bool pin2Invert, bool pin3Invert, bool pin4Invert, bool enableInvert)
|
||||
/// to allow inversion of 2, 3 and 4 wire stepper pins. Requested by Oleg.
|
||||
/// \version 1.35 Removed default args from setPinsInverted(bool, bool, bool, bool, bool) to prevent ambiguity with
|
||||
/// setPinsInverted(bool, bool, bool). Reported by Mac Mac.
|
||||
/// \version 1.36 Changed enableOutputs() and disableOutputs() to be virtual so can be overridden.
|
||||
/// Added new optional argument 'enable' to constructor, which allows you toi disable the
|
||||
/// automatic enabling of outputs at construction time. Suggested by Guido.
|
||||
/// \version 1.37 Fixed a problem with step1 that could cause a rogue step in the
|
||||
/// wrong direction (or not,
|
||||
/// depending on the setup-time requirements of the connected hardware).
|
||||
/// Reported by Mark Tillotson.
|
||||
/// \version 1.38 run() function incorrectly always returned true. Updated function and doc so it returns true
|
||||
/// if the motor is still running to the target position.
|
||||
/// \version 1.39 Updated typos in keywords.txt, courtesey Jon Magill.
|
||||
/// \version 1.40 Updated documentation, including testing on Teensy 3.1
|
||||
/// \version 1.41 Fixed an error in the acceleration calculations, resulting in acceleration of haldf the intended value
|
||||
/// \version 1.42 Improved support for FULL3WIRE and HALF3WIRE output pins. These changes were in Yuri's original
|
||||
/// contribution but did not make it into production.<br>
|
||||
/// \version 1.43 Added DualMotorShield example. Shows how to use AccelStepper to control 2 x 2 phase steppers using the
|
||||
/// Itead Studio Arduino Dual Stepper Motor Driver Shield model IM120417015.<br>
|
||||
/// \version 1.44 examples/DualMotorShield/DualMotorShield.ino examples/DualMotorShield/DualMotorShield.pde
|
||||
/// was missing from the distribution.<br>
|
||||
/// \version 1.45 Fixed a problem where if setAcceleration was not called, there was no default
|
||||
/// acceleration. Reported by Michael Newman.<br>
|
||||
/// \version 1.45 Fixed inaccuracy in acceleration rate by using Equation 15, suggested by Sebastian Gracki.<br>
|
||||
/// Performance improvements in runSpeed suggested by Jaakko Fagerlund.<br>
|
||||
/// \version 1.46 Fixed error in documentation for runToPosition().
|
||||
/// Reinstated time calculations in runSpeed() since new version is reported
|
||||
/// not to work correctly under some circumstances. Reported by Oleg V Gavva.<br>
|
||||
|
||||
///
|
||||
/// \author Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY: USE THE LISTS
|
||||
// Copyright (C) 2009-2013 Mike McCauley
|
||||
// $Id: AccelStepper.h,v 1.21 2014/10/31 06:05:30 mikem Exp mikem $
|
||||
|
||||
#ifndef AccelStepper_h
|
||||
#define AccelStepper_h
|
||||
|
||||
#include <stdlib.h>
|
||||
#if ARDUINO >= 100
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <WProgram.h>
|
||||
#include <wiring.h>
|
||||
#endif
|
||||
|
||||
// These defs cause trouble on some versions of Arduino
|
||||
#undef round
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// \class AccelStepper AccelStepper.h <AccelStepper.h>
|
||||
/// \brief Support for stepper motors with acceleration etc.
|
||||
///
|
||||
/// This defines a single 2 or 4 pin stepper motor, or stepper moter with fdriver chip, with optional
|
||||
/// acceleration, deceleration, absolute positioning commands etc. Multiple
|
||||
/// simultaneous steppers are supported, all moving
|
||||
/// at different speeds and accelerations.
|
||||
///
|
||||
/// \par Operation
|
||||
/// This module operates by computing a step time in microseconds. The step
|
||||
/// time is recomputed after each step and after speed and acceleration
|
||||
/// parameters are changed by the caller. The time of each step is recorded in
|
||||
/// microseconds. The run() function steps the motor once if a new step is due.
|
||||
/// The run() function must be called frequently until the motor is in the
|
||||
/// desired position, after which time run() will do nothing.
|
||||
///
|
||||
/// \par Positioning
|
||||
/// Positions are specified by a signed long integer. At
|
||||
/// construction time, the current position of the motor is consider to be 0. Positive
|
||||
/// positions are clockwise from the initial position; negative positions are
|
||||
/// anticlockwise. The current position can be altered for instance after
|
||||
/// initialization positioning.
|
||||
///
|
||||
/// \par Caveats
|
||||
/// This is an open loop controller: If the motor stalls or is oversped,
|
||||
/// AccelStepper will not have a correct
|
||||
/// idea of where the motor really is (since there is no feedback of the motor's
|
||||
/// real position. We only know where we _think_ it is, relative to the
|
||||
/// initial starting point).
|
||||
///
|
||||
/// \par Performance
|
||||
/// The fastest motor speed that can be reliably supported is about 4000 steps per
|
||||
/// second at a clock frequency of 16 MHz on Arduino such as Uno etc.
|
||||
/// Faster processors can support faster stepping speeds.
|
||||
/// However, any speed less than that
|
||||
/// down to very slow speeds (much less than one per second) are also supported,
|
||||
/// provided the run() function is called frequently enough to step the motor
|
||||
/// whenever required for the speed set.
|
||||
/// Calling setAcceleration() is expensive,
|
||||
/// since it requires a square root to be calculated.
|
||||
class AccelStepper
|
||||
{
|
||||
public:
|
||||
/// \brief Symbolic names for number of pins.
|
||||
/// Use this in the pins argument the AccelStepper constructor to
|
||||
/// provide a symbolic name for the number of pins
|
||||
/// to use.
|
||||
typedef enum
|
||||
{
|
||||
FUNCTION = 0, ///< Use the functional interface, implementing your own driver functions (internal use only)
|
||||
DRIVER = 1, ///< Stepper Driver, 2 driver pins required
|
||||
FULL2WIRE = 2, ///< 2 wire stepper, 2 motor pins required
|
||||
FULL3WIRE = 3, ///< 3 wire stepper, such as HDD spindle, 3 motor pins required
|
||||
FULL4WIRE = 4, ///< 4 wire full stepper, 4 motor pins required
|
||||
HALF3WIRE = 6, ///< 3 wire half stepper, such as HDD spindle, 3 motor pins required
|
||||
HALF4WIRE = 8 ///< 4 wire half stepper, 4 motor pins required
|
||||
} MotorInterfaceType;
|
||||
|
||||
/// Constructor. You can have multiple simultaneous steppers, all moving
|
||||
/// at different speeds and accelerations, provided you call their run()
|
||||
/// functions at frequent enough intervals. Current Position is set to 0, target
|
||||
/// position is set to 0. MaxSpeed and Acceleration default to 1.0.
|
||||
/// The motor pins will be initialised to OUTPUT mode during the
|
||||
/// constructor by a call to enableOutputs().
|
||||
/// \param[in] interface Number of pins to interface to. 1, 2, 4 or 8 are
|
||||
/// supported, but it is preferred to use the \ref MotorInterfaceType symbolic names.
|
||||
/// AccelStepper::DRIVER (1) means a stepper driver (with Step and Direction pins).
|
||||
/// If an enable line is also needed, call setEnablePin() after construction.
|
||||
/// You may also invert the pins using setPinsInverted().
|
||||
/// AccelStepper::FULL2WIRE (2) means a 2 wire stepper (2 pins required).
|
||||
/// AccelStepper::FULL3WIRE (3) means a 3 wire stepper, such as HDD spindle (3 pins required).
|
||||
/// AccelStepper::FULL4WIRE (4) means a 4 wire stepper (4 pins required).
|
||||
/// AccelStepper::HALF3WIRE (6) means a 3 wire half stepper, such as HDD spindle (3 pins required)
|
||||
/// AccelStepper::HALF4WIRE (8) means a 4 wire half stepper (4 pins required)
|
||||
/// Defaults to AccelStepper::FULL4WIRE (4) pins.
|
||||
/// \param[in] pin1 Arduino digital pin number for motor pin 1. Defaults
|
||||
/// to pin 2. For a AccelStepper::DRIVER (interface==1),
|
||||
/// this is the Step input to the driver. Low to high transition means to step)
|
||||
/// \param[in] pin2 Arduino digital pin number for motor pin 2. Defaults
|
||||
/// to pin 3. For a AccelStepper::DRIVER (interface==1),
|
||||
/// this is the Direction input the driver. High means forward.
|
||||
/// \param[in] pin3 Arduino digital pin number for motor pin 3. Defaults
|
||||
/// to pin 4.
|
||||
/// \param[in] pin4 Arduino digital pin number for motor pin 4. Defaults
|
||||
/// to pin 5.
|
||||
/// \param[in] enable If this is true (the default), enableOutputs() will be called to enable
|
||||
/// the output pins at construction time.
|
||||
AccelStepper(uint8_t interface = AccelStepper::FULL4WIRE, uint8_t pin1 = 2, uint8_t pin2 = 3, uint8_t pin3 = 4, uint8_t pin4 = 5, bool enable = true);
|
||||
|
||||
/// Alternate Constructor which will call your own functions for forward and backward steps.
|
||||
/// You can have multiple simultaneous steppers, all moving
|
||||
/// at different speeds and accelerations, provided you call their run()
|
||||
/// functions at frequent enough intervals. Current Position is set to 0, target
|
||||
/// position is set to 0. MaxSpeed and Acceleration default to 1.0.
|
||||
/// Any motor initialization should happen before hand, no pins are used or initialized.
|
||||
/// \param[in] forward void-returning procedure that will make a forward step
|
||||
/// \param[in] backward void-returning procedure that will make a backward step
|
||||
AccelStepper(void (*forward)(), void (*backward)());
|
||||
|
||||
/// Set the target position. The run() function will try to move the motor (at most one step per call)
|
||||
/// from the current position to the target position set by the most
|
||||
/// recent call to this function. Caution: moveTo() also recalculates the speed for the next step.
|
||||
/// If you are trying to use constant speed movements, you should call setSpeed() after calling moveTo().
|
||||
/// \param[in] absolute The desired absolute position. Negative is
|
||||
/// anticlockwise from the 0 position.
|
||||
void moveTo(long absolute);
|
||||
|
||||
/// Set the target position relative to the current position
|
||||
/// \param[in] relative The desired position relative to the current position. Negative is
|
||||
/// anticlockwise from the current position.
|
||||
void move(long relative);
|
||||
|
||||
/// Poll the motor and step it if a step is due, implementing
|
||||
/// accelerations and decelerations to acheive the target position. You must call this as
|
||||
/// frequently as possible, but at least once per minimum step time interval,
|
||||
/// preferably in your main loop. Note that each call to run() will make at most one step, and then only when a step is due,
|
||||
/// based on the current speed and the time since the last step.
|
||||
/// \return true if the motor is still running to the target position.
|
||||
boolean run();
|
||||
|
||||
/// Poll the motor and step it if a step is due, implementing a constant
|
||||
/// speed as set by the most recent call to setSpeed(). You must call this as
|
||||
/// frequently as possible, but at least once per step interval,
|
||||
/// \return true if the motor was stepped.
|
||||
boolean runSpeed();
|
||||
|
||||
/// Sets the maximum permitted speed. The run() function will accelerate
|
||||
/// up to the speed set by this function.
|
||||
/// Caution: the maximum speed achievable depends on your processor and clock speed.
|
||||
/// \param[in] speed The desired maximum speed in steps per second. Must
|
||||
/// be > 0. Caution: Speeds that exceed the maximum speed supported by the processor may
|
||||
/// Result in non-linear accelerations and decelerations.
|
||||
void setMaxSpeed(float speed);
|
||||
|
||||
/// Sets the acceleration/deceleration rate.
|
||||
/// \param[in] acceleration The desired acceleration in steps per second
|
||||
/// per second. Must be > 0.0. This is an expensive call since it requires a square
|
||||
/// root to be calculated. Dont call more ofthen than needed
|
||||
void setAcceleration(float acceleration);
|
||||
|
||||
/// Sets the desired constant speed for use with runSpeed().
|
||||
/// \param[in] speed The desired constant speed in steps per
|
||||
/// second. Positive is clockwise. Speeds of more than 1000 steps per
|
||||
/// second are unreliable. Very slow speeds may be set (eg 0.00027777 for
|
||||
/// once per hour, approximately. Speed accuracy depends on the Arduino
|
||||
/// crystal. Jitter depends on how frequently you call the runSpeed() function.
|
||||
void setSpeed(float speed);
|
||||
|
||||
/// The most recently set speed
|
||||
/// \return the most recent speed in steps per second
|
||||
float speed();
|
||||
|
||||
/// The distance from the current position to the target position.
|
||||
/// \return the distance from the current position to the target position
|
||||
/// in steps. Positive is clockwise from the current position.
|
||||
long distanceToGo();
|
||||
|
||||
/// The most recently set target position.
|
||||
/// \return the target position
|
||||
/// in steps. Positive is clockwise from the 0 position.
|
||||
long targetPosition();
|
||||
|
||||
/// The currently motor position.
|
||||
/// \return the current motor position
|
||||
/// in steps. Positive is clockwise from the 0 position.
|
||||
long currentPosition();
|
||||
|
||||
/// Resets the current position of the motor, so that wherever the motor
|
||||
/// happens to be right now is considered to be the new 0 position. Useful
|
||||
/// for setting a zero position on a stepper after an initial hardware
|
||||
/// positioning move.
|
||||
/// Has the side effect of setting the current motor speed to 0.
|
||||
/// \param[in] position The position in steps of wherever the motor
|
||||
/// happens to be right now.
|
||||
void setCurrentPosition(long position);
|
||||
|
||||
/// Moves the motor (with acceleration/deceleration)
|
||||
/// to the target position and blocks until it is at
|
||||
/// position. Dont use this in event loops, since it blocks.
|
||||
void runToPosition();
|
||||
|
||||
/// Runs at the currently selected speed until the target position is reached
|
||||
/// Does not implement accelerations.
|
||||
/// \return true if it stepped
|
||||
boolean runSpeedToPosition();
|
||||
|
||||
/// Moves the motor (with acceleration/deceleration)
|
||||
/// to the new target position and blocks until it is at
|
||||
/// position. Dont use this in event loops, since it blocks.
|
||||
/// \param[in] position The new target position.
|
||||
void runToNewPosition(long position);
|
||||
|
||||
/// Sets a new target position that causes the stepper
|
||||
/// to stop as quickly as possible, using the current speed and acceleration parameters.
|
||||
void stop();
|
||||
|
||||
/// Disable motor pin outputs by setting them all LOW
|
||||
/// Depending on the design of your electronics this may turn off
|
||||
/// the power to the motor coils, saving power.
|
||||
/// This is useful to support Arduino low power modes: disable the outputs
|
||||
/// during sleep and then reenable with enableOutputs() before stepping
|
||||
/// again.
|
||||
virtual void disableOutputs();
|
||||
|
||||
/// Enable motor pin outputs by setting the motor pins to OUTPUT
|
||||
/// mode. Called automatically by the constructor.
|
||||
virtual void enableOutputs();
|
||||
|
||||
/// Sets the minimum pulse width allowed by the stepper driver. The minimum practical pulse width is
|
||||
/// approximately 20 microseconds. Times less than 20 microseconds
|
||||
/// will usually result in 20 microseconds or so.
|
||||
/// \param[in] minWidth The minimum pulse width in microseconds.
|
||||
void setMinPulseWidth(unsigned int minWidth);
|
||||
|
||||
/// Sets the enable pin number for stepper drivers.
|
||||
/// 0xFF indicates unused (default).
|
||||
/// Otherwise, if a pin is set, the pin will be turned on when
|
||||
/// enableOutputs() is called and switched off when disableOutputs()
|
||||
/// is called.
|
||||
/// \param[in] enablePin Arduino digital pin number for motor enable
|
||||
/// \sa setPinsInverted
|
||||
void setEnablePin(uint8_t enablePin = 0xff);
|
||||
|
||||
/// Sets the inversion for stepper driver pins
|
||||
/// \param[in] directionInvert True for inverted direction pin, false for non-inverted
|
||||
/// \param[in] stepInvert True for inverted step pin, false for non-inverted
|
||||
/// \param[in] enableInvert True for inverted enable pin, false (default) for non-inverted
|
||||
void setPinsInverted(bool directionInvert = false, bool stepInvert = false, bool enableInvert = false);
|
||||
|
||||
/// Sets the inversion for 2, 3 and 4 wire stepper pins
|
||||
/// \param[in] pin1Invert True for inverted pin1, false for non-inverted
|
||||
/// \param[in] pin2Invert True for inverted pin2, false for non-inverted
|
||||
/// \param[in] pin3Invert True for inverted pin3, false for non-inverted
|
||||
/// \param[in] pin4Invert True for inverted pin4, false for non-inverted
|
||||
/// \param[in] enableInvert True for inverted enable pin, false (default) for non-inverted
|
||||
void setPinsInverted(bool pin1Invert, bool pin2Invert, bool pin3Invert, bool pin4Invert, bool enableInvert);
|
||||
|
||||
protected:
|
||||
|
||||
/// \brief Direction indicator
|
||||
/// Symbolic names for the direction the motor is turning
|
||||
typedef enum
|
||||
{
|
||||
DIRECTION_CCW = 0, ///< Clockwise
|
||||
DIRECTION_CW = 1 ///< Counter-Clockwise
|
||||
} Direction;
|
||||
|
||||
/// Forces the library to compute a new instantaneous speed and set that as
|
||||
/// the current speed. It is called by
|
||||
/// the library:
|
||||
/// \li after each step
|
||||
/// \li after change to maxSpeed through setMaxSpeed()
|
||||
/// \li after change to acceleration through setAcceleration()
|
||||
/// \li after change to target position (relative or absolute) through
|
||||
/// move() or moveTo()
|
||||
void computeNewSpeed();
|
||||
|
||||
/// Low level function to set the motor output pins
|
||||
/// bit 0 of the mask corresponds to _pin[0]
|
||||
/// bit 1 of the mask corresponds to _pin[1]
|
||||
/// You can override this to impment, for example serial chip output insted of using the
|
||||
/// output pins directly
|
||||
virtual void setOutputPins(uint8_t mask);
|
||||
|
||||
/// Called to execute a step. Only called when a new step is
|
||||
/// required. Subclasses may override to implement new stepping
|
||||
/// interfaces. The default calls step1(), step2(), step4() or step8() depending on the
|
||||
/// number of pins defined for the stepper.
|
||||
/// \param[in] step The current step phase number (0 to 7)
|
||||
virtual void step(long step);
|
||||
|
||||
/// Called to execute a step using stepper functions (pins = 0) Only called when a new step is
|
||||
/// required. Calls _forward() or _backward() to perform the step
|
||||
/// \param[in] step The current step phase number (0 to 7)
|
||||
virtual void step0(long step);
|
||||
|
||||
/// Called to execute a step on a stepper driver (ie where pins == 1). Only called when a new step is
|
||||
/// required. Subclasses may override to implement new stepping
|
||||
/// interfaces. The default sets or clears the outputs of Step pin1 to step,
|
||||
/// and sets the output of _pin2 to the desired direction. The Step pin (_pin1) is pulsed for 1 microsecond
|
||||
/// which is the minimum STEP pulse width for the 3967 driver.
|
||||
/// \param[in] step The current step phase number (0 to 7)
|
||||
virtual void step1(long step);
|
||||
|
||||
/// Called to execute a step on a 2 pin motor. Only called when a new step is
|
||||
/// required. Subclasses may override to implement new stepping
|
||||
/// interfaces. The default sets or clears the outputs of pin1 and pin2
|
||||
/// \param[in] step The current step phase number (0 to 7)
|
||||
virtual void step2(long step);
|
||||
|
||||
/// Called to execute a step on a 3 pin motor, such as HDD spindle. Only called when a new step is
|
||||
/// required. Subclasses may override to implement new stepping
|
||||
/// interfaces. The default sets or clears the outputs of pin1, pin2,
|
||||
/// pin3
|
||||
/// \param[in] step The current step phase number (0 to 7)
|
||||
virtual void step3(long step);
|
||||
|
||||
/// Called to execute a step on a 4 pin motor. Only called when a new step is
|
||||
/// required. Subclasses may override to implement new stepping
|
||||
/// interfaces. The default sets or clears the outputs of pin1, pin2,
|
||||
/// pin3, pin4.
|
||||
/// \param[in] step The current step phase number (0 to 7)
|
||||
virtual void step4(long step);
|
||||
|
||||
/// Called to execute a step on a 3 pin motor, such as HDD spindle. Only called when a new step is
|
||||
/// required. Subclasses may override to implement new stepping
|
||||
/// interfaces. The default sets or clears the outputs of pin1, pin2,
|
||||
/// pin3
|
||||
/// \param[in] step The current step phase number (0 to 7)
|
||||
virtual void step6(long step);
|
||||
|
||||
/// Called to execute a step on a 4 pin half-steper motor. Only called when a new step is
|
||||
/// required. Subclasses may override to implement new stepping
|
||||
/// interfaces. The default sets or clears the outputs of pin1, pin2,
|
||||
/// pin3, pin4.
|
||||
/// \param[in] step The current step phase number (0 to 7)
|
||||
virtual void step8(long step);
|
||||
|
||||
private:
|
||||
/// Number of pins on the stepper motor. Permits 2 or 4. 2 pins is a
|
||||
/// bipolar, and 4 pins is a unipolar.
|
||||
uint8_t _interface; // 0, 1, 2, 4, 8, See MotorInterfaceType
|
||||
|
||||
/// Arduino pin number assignments for the 2 or 4 pins required to interface to the
|
||||
/// stepper motor or driver
|
||||
uint8_t _pin[4];
|
||||
|
||||
/// Whether the _pins is inverted or not
|
||||
uint8_t _pinInverted[4];
|
||||
|
||||
/// The current absolution position in steps.
|
||||
long _currentPos; // Steps
|
||||
|
||||
/// The target position in steps. The AccelStepper library will move the
|
||||
/// motor from the _currentPos to the _targetPos, taking into account the
|
||||
/// max speed, acceleration and deceleration
|
||||
long _targetPos; // Steps
|
||||
|
||||
/// The current motos speed in steps per second
|
||||
/// Positive is clockwise
|
||||
float _speed; // Steps per second
|
||||
|
||||
/// The maximum permitted speed in steps per second. Must be > 0.
|
||||
float _maxSpeed;
|
||||
|
||||
/// The acceleration to use to accelerate or decelerate the motor in steps
|
||||
/// per second per second. Must be > 0
|
||||
float _acceleration;
|
||||
float _sqrt_twoa; // Precomputed sqrt(2*_acceleration)
|
||||
|
||||
/// The current interval between steps in microseconds.
|
||||
/// 0 means the motor is currently stopped with _speed == 0
|
||||
unsigned long _stepInterval;
|
||||
|
||||
/// The last step time in microseconds
|
||||
unsigned long _lastStepTime;
|
||||
|
||||
/// The minimum allowed pulse width in microseconds
|
||||
unsigned int _minPulseWidth;
|
||||
|
||||
/// Is the direction pin inverted?
|
||||
///bool _dirInverted; /// Moved to _pinInverted[1]
|
||||
|
||||
/// Is the step pin inverted?
|
||||
///bool _stepInverted; /// Moved to _pinInverted[0]
|
||||
|
||||
/// Is the enable pin inverted?
|
||||
bool _enableInverted;
|
||||
|
||||
/// Enable pin for stepper driver, or 0xFF if unused.
|
||||
uint8_t _enablePin;
|
||||
|
||||
/// The pointer to a forward-step procedure
|
||||
void (*_forward)();
|
||||
|
||||
/// The pointer to a backward-step procedure
|
||||
void (*_backward)();
|
||||
|
||||
/// The step counter for speed calculations
|
||||
long _n;
|
||||
|
||||
/// Initial step size in microseconds
|
||||
float _c0;
|
||||
|
||||
/// Last step size in microseconds
|
||||
float _cn;
|
||||
|
||||
/// Min step size in microseconds based on maxSpeed
|
||||
float _cmin; // at max speed
|
||||
|
||||
/// Current direction motor is spinning in
|
||||
boolean _direction; // 1 == CW
|
||||
|
||||
};
|
||||
|
||||
/// @example Random.pde
|
||||
/// Make a single stepper perform random changes in speed, position and acceleration
|
||||
|
||||
/// @example Overshoot.pde
|
||||
/// Check overshoot handling
|
||||
/// which sets a new target position and then waits until the stepper has
|
||||
/// achieved it. This is used for testing the handling of overshoots
|
||||
|
||||
/// @example MultiStepper.pde
|
||||
/// Shows how to multiple simultaneous steppers
|
||||
/// Runs one stepper forwards and backwards, accelerating and decelerating
|
||||
/// at the limits. Runs other steppers at the same time
|
||||
|
||||
/// @example ConstantSpeed.pde
|
||||
/// Shows how to run AccelStepper in the simplest,
|
||||
/// fixed speed mode with no accelerations
|
||||
|
||||
/// @example Blocking.pde
|
||||
/// Shows how to use the blocking call runToNewPosition
|
||||
/// Which sets a new target position and then waits until the stepper has
|
||||
/// achieved it.
|
||||
|
||||
/// @example AFMotor_MultiStepper.pde
|
||||
/// Control both Stepper motors at the same time with different speeds
|
||||
/// and accelerations.
|
||||
|
||||
/// @example AFMotor_ConstantSpeed.pde
|
||||
/// Shows how to run AccelStepper in the simplest,
|
||||
/// fixed speed mode with no accelerations
|
||||
|
||||
/// @example ProportionalControl.pde
|
||||
/// Make a single stepper follow the analog value read from a pot or whatever
|
||||
/// The stepper will move at a constant speed to each newly set posiiton,
|
||||
/// depending on the value of the pot.
|
||||
|
||||
/// @example Bounce.pde
|
||||
/// Make a single stepper bounce from one limit to another, observing
|
||||
/// accelrations at each end of travel
|
||||
|
||||
/// @example Quickstop.pde
|
||||
/// Check stop handling.
|
||||
/// Calls stop() while the stepper is travelling at full speed, causing
|
||||
/// the stepper to stop as quickly as possible, within the constraints of the
|
||||
/// current acceleration.
|
||||
|
||||
/// @example MotorShield.pde
|
||||
/// Shows how to use AccelStepper to control a 3-phase motor, such as a HDD spindle motor
|
||||
/// using the Adafruit Motor Shield http://www.ladyada.net/make/mshield/index.html.
|
||||
|
||||
/// @example DualMotorShield.pde
|
||||
/// Shows how to use AccelStepper to control 2 x 2 phase steppers using the
|
||||
/// Itead Studio Arduino Dual Stepper Motor Driver Shield
|
||||
/// model IM120417015
|
||||
|
||||
#endif
|
||||
848
CAD/Zusatzteile-EiAndockerLinksORing16x2.step
Normal file
848
CAD/Zusatzteile-EiAndockerLinksORing16x2.step
Normal file
@@ -0,0 +1,848 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('FreeCAD Model'),'2;1');
|
||||
FILE_NAME('Open CASCADE Shape Model','2026-03-16T19:11:20',(''),(''),
|
||||
'Open CASCADE STEP processor 7.8','FreeCAD','Unknown');
|
||||
FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1 = APPLICATION_PROTOCOL_DEFINITION('international standard',
|
||||
'automotive_design',2000,#2);
|
||||
#2 = APPLICATION_CONTEXT(
|
||||
'core data for automotive mechanical design processes');
|
||||
#3 = SHAPE_DEFINITION_REPRESENTATION(#4,#10);
|
||||
#4 = PRODUCT_DEFINITION_SHAPE('','',#5);
|
||||
#5 = PRODUCT_DEFINITION('design','',#6,#9);
|
||||
#6 = PRODUCT_DEFINITION_FORMATION('','',#7);
|
||||
#7 = PRODUCT('EiAndockerLinksORing16x2','EiAndockerLinksORing16x2','',(
|
||||
#8));
|
||||
#8 = PRODUCT_CONTEXT('',#2,'mechanical');
|
||||
#9 = PRODUCT_DEFINITION_CONTEXT('part definition',#2,'design');
|
||||
#10 = ADVANCED_BREP_SHAPE_REPRESENTATION('',(#11,#15),#629);
|
||||
#11 = AXIS2_PLACEMENT_3D('',#12,#13,#14);
|
||||
#12 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#13 = DIRECTION('',(0.,0.,1.));
|
||||
#14 = DIRECTION('',(1.,0.,-0.));
|
||||
#15 = MANIFOLD_SOLID_BREP('',#16);
|
||||
#16 = CLOSED_SHELL('',(#17,#241,#269,#295,#322,#354,#404,#424,#444,#491,
|
||||
#502,#519,#529,#539,#565,#595,#620));
|
||||
#17 = ADVANCED_FACE('',(#18,#45),#236,.T.);
|
||||
#18 = FACE_BOUND('',#19,.F.);
|
||||
#19 = EDGE_LOOP('',(#20,#30,#37,#38));
|
||||
#20 = ORIENTED_EDGE('',*,*,#21,.T.);
|
||||
#21 = EDGE_CURVE('',#22,#24,#26,.T.);
|
||||
#22 = VERTEX_POINT('',#23);
|
||||
#23 = CARTESIAN_POINT('',(11.,0.,0.));
|
||||
#24 = VERTEX_POINT('',#25);
|
||||
#25 = CARTESIAN_POINT('',(11.,0.,39.));
|
||||
#26 = LINE('',#27,#28);
|
||||
#27 = CARTESIAN_POINT('',(11.,0.,0.));
|
||||
#28 = VECTOR('',#29,1.);
|
||||
#29 = DIRECTION('',(0.,0.,1.));
|
||||
#30 = ORIENTED_EDGE('',*,*,#31,.T.);
|
||||
#31 = EDGE_CURVE('',#24,#24,#32,.T.);
|
||||
#32 = CIRCLE('',#33,11.);
|
||||
#33 = AXIS2_PLACEMENT_3D('',#34,#35,#36);
|
||||
#34 = CARTESIAN_POINT('',(0.,0.,39.));
|
||||
#35 = DIRECTION('',(0.,0.,1.));
|
||||
#36 = DIRECTION('',(1.,0.,0.));
|
||||
#37 = ORIENTED_EDGE('',*,*,#21,.F.);
|
||||
#38 = ORIENTED_EDGE('',*,*,#39,.F.);
|
||||
#39 = EDGE_CURVE('',#22,#22,#40,.T.);
|
||||
#40 = CIRCLE('',#41,11.);
|
||||
#41 = AXIS2_PLACEMENT_3D('',#42,#43,#44);
|
||||
#42 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#43 = DIRECTION('',(0.,0.,1.));
|
||||
#44 = DIRECTION('',(1.,0.,0.));
|
||||
#45 = FACE_BOUND('',#46,.F.);
|
||||
#46 = EDGE_LOOP('',(#47,#112,#175));
|
||||
#47 = ORIENTED_EDGE('',*,*,#48,.F.);
|
||||
#48 = EDGE_CURVE('',#49,#51,#53,.T.);
|
||||
#49 = VERTEX_POINT('',#50);
|
||||
#50 = CARTESIAN_POINT('',(-2.927613660003E-07,-11.,12.));
|
||||
#51 = VERTEX_POINT('',#52);
|
||||
#52 = CARTESIAN_POINT('',(2.374854292069E-07,-11.,6.));
|
||||
#53 = B_SPLINE_CURVE_WITH_KNOTS('',8,(#54,#55,#56,#57,#58,#59,#60,#61,
|
||||
#62,#63,#64,#65,#66,#67,#68,#69,#70,#71,#72,#73,#74,#75,#76,#77,#78,
|
||||
#79,#80,#81,#82,#83,#84,#85,#86,#87,#88,#89,#90,#91,#92,#93,#94,#95,
|
||||
#96,#97,#98,#99,#100,#101,#102,#103,#104,#105,#106,#107,#108,#109,
|
||||
#110,#111),.UNSPECIFIED.,.T.,.F.,(9,7,7,7,7,7,7,7,9),(0.,
|
||||
0.12028008765,0.221803874722,0.313717691764,0.424279510118,
|
||||
0.557035521011,0.678561824844,0.82156830206,1.),.UNSPECIFIED.);
|
||||
#54 = CARTESIAN_POINT('',(-3.,-10.58300524425,9.));
|
||||
#55 = CARTESIAN_POINT('',(-3.,-10.58300524425,9.247784463312));
|
||||
#56 = CARTESIAN_POINT('',(-2.97661099072,-10.58963540912,9.495313801967)
|
||||
);
|
||||
#57 = CARTESIAN_POINT('',(-2.929914976415,-10.60287246882,9.741235870322
|
||||
));
|
||||
#58 = CARTESIAN_POINT('',(-2.859695742185,-10.62246505723,9.984131705533
|
||||
));
|
||||
#59 = CARTESIAN_POINT('',(-2.765485670196,-10.64792558084,
|
||||
10.222409636621));
|
||||
#60 = CARTESIAN_POINT('',(-2.646608254922,-10.67848833322,
|
||||
10.454173492424));
|
||||
#61 = CARTESIAN_POINT('',(-2.502230313136,-10.71304744389,
|
||||
10.677050088509));
|
||||
#62 = CARTESIAN_POINT('',(-2.187278576625,-10.78135296782,
|
||||
11.065983092374));
|
||||
#63 = CARTESIAN_POINT('',(-2.023097625341,-10.8146465199,11.236965633107
|
||||
));
|
||||
#64 = CARTESIAN_POINT('',(-1.838511950113,-10.84895509728,
|
||||
11.397919728876));
|
||||
#65 = CARTESIAN_POINT('',(-1.63351040545,-10.88295677609,11.545684384537
|
||||
));
|
||||
#66 = CARTESIAN_POINT('',(-1.408464110369,-10.9150703833,11.676917729215
|
||||
));
|
||||
#67 = CARTESIAN_POINT('',(-1.164167045979,-10.9435285676,11.788067939419
|
||||
));
|
||||
#68 = CARTESIAN_POINT('',(-0.901928016914,-10.96650203127,
|
||||
11.875311066446));
|
||||
#69 = CARTESIAN_POINT('',(-0.371875127732,-10.99660615524,
|
||||
11.987977286074));
|
||||
#70 = CARTESIAN_POINT('',(-0.110970775212,-11.00480136742,
|
||||
12.017613390432));
|
||||
#71 = CARTESIAN_POINT('',(0.154044903682,-11.00581158676,12.020939532078
|
||||
));
|
||||
#72 = CARTESIAN_POINT('',(0.417899218976,-10.99920605305,11.997223641124
|
||||
));
|
||||
#73 = CARTESIAN_POINT('',(0.675301956574,-10.98540660424,11.947326541564
|
||||
));
|
||||
#74 = CARTESIAN_POINT('',(0.921256391971,-10.96563060757,11.873683621587
|
||||
));
|
||||
#75 = CARTESIAN_POINT('',(1.151344483982,-10.9416388314,11.780339397179)
|
||||
);
|
||||
#76 = CARTESIAN_POINT('',(1.615331728924,-10.88374449378,11.543926087641
|
||||
));
|
||||
#77 = CARTESIAN_POINT('',(1.844790555774,-10.84828787337,11.392441050153
|
||||
));
|
||||
#78 = CARTESIAN_POINT('',(2.050308680927,-10.81116823604,11.22331735385)
|
||||
);
|
||||
#79 = CARTESIAN_POINT('',(2.232359248194,-10.77424422358,11.040306652498
|
||||
));
|
||||
#80 = CARTESIAN_POINT('',(2.391662754536,-10.73898686411,10.846382998433
|
||||
));
|
||||
#81 = CARTESIAN_POINT('',(2.529009637688,-10.70651764133,10.643900023181
|
||||
));
|
||||
#82 = CARTESIAN_POINT('',(2.645138305672,-10.67768546127,10.434702100122
|
||||
));
|
||||
#83 = CARTESIAN_POINT('',(2.85530473354,-10.62361774287,9.962660574488)
|
||||
);
|
||||
#84 = CARTESIAN_POINT('',(2.940294167414,-10.60024937474,9.697330811818)
|
||||
);
|
||||
#85 = CARTESIAN_POINT('',(2.996436520884,-10.58414357274,9.426929463329)
|
||||
);
|
||||
#86 = CARTESIAN_POINT('',(3.024268131702,-10.575989631,9.153706681797));
|
||||
#87 = CARTESIAN_POINT('',(3.024041649892,-10.57609184962,8.87967057245)
|
||||
);
|
||||
#88 = CARTESIAN_POINT('',(2.995725631373,-10.58439583235,8.606771307622)
|
||||
);
|
||||
#89 = CARTESIAN_POINT('',(2.938989259398,-10.60048293223,8.337074927084)
|
||||
);
|
||||
#90 = CARTESIAN_POINT('',(2.77460924759,-10.64463059066,7.831165270903)
|
||||
);
|
||||
#91 = CARTESIAN_POINT('',(2.67175104555,-10.67154326208,7.59427485792));
|
||||
#92 = CARTESIAN_POINT('',(2.544056330611,-10.70365056993,7.363762211941)
|
||||
);
|
||||
#93 = CARTESIAN_POINT('',(2.390660235376,-10.74006998818,7.141595617126)
|
||||
);
|
||||
#94 = CARTESIAN_POINT('',(2.210506350817,-10.7795878533,6.930367243669)
|
||||
);
|
||||
#95 = CARTESIAN_POINT('',(2.002505624649,-10.82052918413,6.733473412639)
|
||||
);
|
||||
#96 = CARTESIAN_POINT('',(1.76577358126,-10.86066377413,6.555368237331)
|
||||
);
|
||||
#97 = CARTESIAN_POINT('',(1.187250065257,-10.94029721233,6.221357529634)
|
||||
);
|
||||
#98 = CARTESIAN_POINT('',(0.823824071924,-10.97986998106,6.068899191573)
|
||||
);
|
||||
#99 = CARTESIAN_POINT('',(0.422202059946,-11.00848612598,5.968367559822)
|
||||
);
|
||||
#100 = CARTESIAN_POINT('',(2.076561145259E-12,-11.01910836895,
|
||||
5.933413112681));
|
||||
#101 = CARTESIAN_POINT('',(-0.42220205995,-11.00848612598,5.968367559823
|
||||
));
|
||||
#102 = CARTESIAN_POINT('',(-0.823824071923,-10.97986998106,
|
||||
6.068899191572));
|
||||
#103 = CARTESIAN_POINT('',(-1.187250065258,-10.94029721233,
|
||||
6.221357529634));
|
||||
#104 = CARTESIAN_POINT('',(-1.890223596673,-10.84353329916,6.62721948723
|
||||
));
|
||||
#105 = CARTESIAN_POINT('',(-2.218095833563,-10.78212870183,
|
||||
6.905826251681));
|
||||
#106 = CARTESIAN_POINT('',(-2.484538511301,-10.72169165657,
|
||||
7.219322097354));
|
||||
#107 = CARTESIAN_POINT('',(-2.692957757048,-10.66854367312,
|
||||
7.555805901262));
|
||||
#108 = CARTESIAN_POINT('',(-2.846758847749,-10.62644198358,7.90703918817
|
||||
));
|
||||
#109 = CARTESIAN_POINT('',(-2.94864197823,-10.59756449647,8.267356983371
|
||||
));
|
||||
#110 = CARTESIAN_POINT('',(-3.,-10.58300524425,8.632829859132));
|
||||
#111 = CARTESIAN_POINT('',(-3.,-10.58300524425,9.));
|
||||
#112 = ORIENTED_EDGE('',*,*,#113,.F.);
|
||||
#113 = EDGE_CURVE('',#114,#49,#116,.T.);
|
||||
#114 = VERTEX_POINT('',#115);
|
||||
#115 = CARTESIAN_POINT('',(-3.,-10.58300524425,9.));
|
||||
#116 = B_SPLINE_CURVE_WITH_KNOTS('',8,(#117,#118,#119,#120,#121,#122,
|
||||
#123,#124,#125,#126,#127,#128,#129,#130,#131,#132,#133,#134,#135,
|
||||
#136,#137,#138,#139,#140,#141,#142,#143,#144,#145,#146,#147,#148,
|
||||
#149,#150,#151,#152,#153,#154,#155,#156,#157,#158,#159,#160,#161,
|
||||
#162,#163,#164,#165,#166,#167,#168,#169,#170,#171,#172,#173,#174),
|
||||
.UNSPECIFIED.,.T.,.F.,(9,7,7,7,7,7,7,7,9),(0.,0.12028008765,
|
||||
0.221803874722,0.313717691764,0.424279510118,0.557035521011,
|
||||
0.678561824844,0.82156830206,1.),.UNSPECIFIED.);
|
||||
#117 = CARTESIAN_POINT('',(-3.,-10.58300524425,9.));
|
||||
#118 = CARTESIAN_POINT('',(-3.,-10.58300524425,9.247784463312));
|
||||
#119 = CARTESIAN_POINT('',(-2.97661099072,-10.58963540912,9.495313801967
|
||||
));
|
||||
#120 = CARTESIAN_POINT('',(-2.929914976415,-10.60287246882,
|
||||
9.741235870322));
|
||||
#121 = CARTESIAN_POINT('',(-2.859695742185,-10.62246505723,
|
||||
9.984131705533));
|
||||
#122 = CARTESIAN_POINT('',(-2.765485670196,-10.64792558084,
|
||||
10.222409636621));
|
||||
#123 = CARTESIAN_POINT('',(-2.646608254922,-10.67848833322,
|
||||
10.454173492424));
|
||||
#124 = CARTESIAN_POINT('',(-2.502230313136,-10.71304744389,
|
||||
10.677050088509));
|
||||
#125 = CARTESIAN_POINT('',(-2.187278576625,-10.78135296782,
|
||||
11.065983092374));
|
||||
#126 = CARTESIAN_POINT('',(-2.023097625341,-10.8146465199,
|
||||
11.236965633107));
|
||||
#127 = CARTESIAN_POINT('',(-1.838511950113,-10.84895509728,
|
||||
11.397919728876));
|
||||
#128 = CARTESIAN_POINT('',(-1.63351040545,-10.88295677609,
|
||||
11.545684384537));
|
||||
#129 = CARTESIAN_POINT('',(-1.408464110369,-10.9150703833,
|
||||
11.676917729215));
|
||||
#130 = CARTESIAN_POINT('',(-1.164167045979,-10.9435285676,
|
||||
11.788067939419));
|
||||
#131 = CARTESIAN_POINT('',(-0.901928016914,-10.96650203127,
|
||||
11.875311066446));
|
||||
#132 = CARTESIAN_POINT('',(-0.371875127732,-10.99660615524,
|
||||
11.987977286074));
|
||||
#133 = CARTESIAN_POINT('',(-0.110970775212,-11.00480136742,
|
||||
12.017613390432));
|
||||
#134 = CARTESIAN_POINT('',(0.154044903682,-11.00581158676,
|
||||
12.020939532078));
|
||||
#135 = CARTESIAN_POINT('',(0.417899218976,-10.99920605305,
|
||||
11.997223641124));
|
||||
#136 = CARTESIAN_POINT('',(0.675301956574,-10.98540660424,
|
||||
11.947326541564));
|
||||
#137 = CARTESIAN_POINT('',(0.921256391971,-10.96563060757,
|
||||
11.873683621587));
|
||||
#138 = CARTESIAN_POINT('',(1.151344483982,-10.9416388314,11.780339397179
|
||||
));
|
||||
#139 = CARTESIAN_POINT('',(1.615331728924,-10.88374449378,
|
||||
11.543926087641));
|
||||
#140 = CARTESIAN_POINT('',(1.844790555774,-10.84828787337,
|
||||
11.392441050153));
|
||||
#141 = CARTESIAN_POINT('',(2.050308680927,-10.81116823604,11.22331735385
|
||||
));
|
||||
#142 = CARTESIAN_POINT('',(2.232359248194,-10.77424422358,
|
||||
11.040306652498));
|
||||
#143 = CARTESIAN_POINT('',(2.391662754536,-10.73898686411,
|
||||
10.846382998433));
|
||||
#144 = CARTESIAN_POINT('',(2.529009637688,-10.70651764133,
|
||||
10.643900023181));
|
||||
#145 = CARTESIAN_POINT('',(2.645138305672,-10.67768546127,
|
||||
10.434702100122));
|
||||
#146 = CARTESIAN_POINT('',(2.85530473354,-10.62361774287,9.962660574488)
|
||||
);
|
||||
#147 = CARTESIAN_POINT('',(2.940294167414,-10.60024937474,9.697330811818
|
||||
));
|
||||
#148 = CARTESIAN_POINT('',(2.996436520884,-10.58414357274,9.426929463329
|
||||
));
|
||||
#149 = CARTESIAN_POINT('',(3.024268131702,-10.575989631,9.153706681797)
|
||||
);
|
||||
#150 = CARTESIAN_POINT('',(3.024041649892,-10.57609184962,8.87967057245)
|
||||
);
|
||||
#151 = CARTESIAN_POINT('',(2.995725631373,-10.58439583235,8.606771307622
|
||||
));
|
||||
#152 = CARTESIAN_POINT('',(2.938989259398,-10.60048293223,8.337074927084
|
||||
));
|
||||
#153 = CARTESIAN_POINT('',(2.77460924759,-10.64463059066,7.831165270903)
|
||||
);
|
||||
#154 = CARTESIAN_POINT('',(2.67175104555,-10.67154326208,7.59427485792)
|
||||
);
|
||||
#155 = CARTESIAN_POINT('',(2.544056330611,-10.70365056993,7.363762211941
|
||||
));
|
||||
#156 = CARTESIAN_POINT('',(2.390660235376,-10.74006998818,7.141595617126
|
||||
));
|
||||
#157 = CARTESIAN_POINT('',(2.210506350817,-10.7795878533,6.930367243669)
|
||||
);
|
||||
#158 = CARTESIAN_POINT('',(2.002505624649,-10.82052918413,6.733473412639
|
||||
));
|
||||
#159 = CARTESIAN_POINT('',(1.76577358126,-10.86066377413,6.555368237331)
|
||||
);
|
||||
#160 = CARTESIAN_POINT('',(1.187250065257,-10.94029721233,6.221357529634
|
||||
));
|
||||
#161 = CARTESIAN_POINT('',(0.823824071924,-10.97986998106,6.068899191573
|
||||
));
|
||||
#162 = CARTESIAN_POINT('',(0.422202059946,-11.00848612598,5.968367559822
|
||||
));
|
||||
#163 = CARTESIAN_POINT('',(2.076561145259E-12,-11.01910836895,
|
||||
5.933413112681));
|
||||
#164 = CARTESIAN_POINT('',(-0.42220205995,-11.00848612598,5.968367559823
|
||||
));
|
||||
#165 = CARTESIAN_POINT('',(-0.823824071923,-10.97986998106,
|
||||
6.068899191572));
|
||||
#166 = CARTESIAN_POINT('',(-1.187250065258,-10.94029721233,
|
||||
6.221357529634));
|
||||
#167 = CARTESIAN_POINT('',(-1.890223596673,-10.84353329916,6.62721948723
|
||||
));
|
||||
#168 = CARTESIAN_POINT('',(-2.218095833563,-10.78212870183,
|
||||
6.905826251681));
|
||||
#169 = CARTESIAN_POINT('',(-2.484538511301,-10.72169165657,
|
||||
7.219322097354));
|
||||
#170 = CARTESIAN_POINT('',(-2.692957757048,-10.66854367312,
|
||||
7.555805901262));
|
||||
#171 = CARTESIAN_POINT('',(-2.846758847749,-10.62644198358,7.90703918817
|
||||
));
|
||||
#172 = CARTESIAN_POINT('',(-2.94864197823,-10.59756449647,8.267356983371
|
||||
));
|
||||
#173 = CARTESIAN_POINT('',(-3.,-10.58300524425,8.632829859132));
|
||||
#174 = CARTESIAN_POINT('',(-3.,-10.58300524425,9.));
|
||||
#175 = ORIENTED_EDGE('',*,*,#176,.F.);
|
||||
#176 = EDGE_CURVE('',#51,#114,#177,.T.);
|
||||
#177 = B_SPLINE_CURVE_WITH_KNOTS('',8,(#178,#179,#180,#181,#182,#183,
|
||||
#184,#185,#186,#187,#188,#189,#190,#191,#192,#193,#194,#195,#196,
|
||||
#197,#198,#199,#200,#201,#202,#203,#204,#205,#206,#207,#208,#209,
|
||||
#210,#211,#212,#213,#214,#215,#216,#217,#218,#219,#220,#221,#222,
|
||||
#223,#224,#225,#226,#227,#228,#229,#230,#231,#232,#233,#234,#235),
|
||||
.UNSPECIFIED.,.T.,.F.,(9,7,7,7,7,7,7,7,9),(0.,0.12028008765,
|
||||
0.221803874722,0.313717691764,0.424279510118,0.557035521011,
|
||||
0.678561824844,0.82156830206,1.),.UNSPECIFIED.);
|
||||
#178 = CARTESIAN_POINT('',(-3.,-10.58300524425,9.));
|
||||
#179 = CARTESIAN_POINT('',(-3.,-10.58300524425,9.247784463312));
|
||||
#180 = CARTESIAN_POINT('',(-2.97661099072,-10.58963540912,9.495313801967
|
||||
));
|
||||
#181 = CARTESIAN_POINT('',(-2.929914976415,-10.60287246882,
|
||||
9.741235870322));
|
||||
#182 = CARTESIAN_POINT('',(-2.859695742185,-10.62246505723,
|
||||
9.984131705533));
|
||||
#183 = CARTESIAN_POINT('',(-2.765485670196,-10.64792558084,
|
||||
10.222409636621));
|
||||
#184 = CARTESIAN_POINT('',(-2.646608254922,-10.67848833322,
|
||||
10.454173492424));
|
||||
#185 = CARTESIAN_POINT('',(-2.502230313136,-10.71304744389,
|
||||
10.677050088509));
|
||||
#186 = CARTESIAN_POINT('',(-2.187278576625,-10.78135296782,
|
||||
11.065983092374));
|
||||
#187 = CARTESIAN_POINT('',(-2.023097625341,-10.8146465199,
|
||||
11.236965633107));
|
||||
#188 = CARTESIAN_POINT('',(-1.838511950113,-10.84895509728,
|
||||
11.397919728876));
|
||||
#189 = CARTESIAN_POINT('',(-1.63351040545,-10.88295677609,
|
||||
11.545684384537));
|
||||
#190 = CARTESIAN_POINT('',(-1.408464110369,-10.9150703833,
|
||||
11.676917729215));
|
||||
#191 = CARTESIAN_POINT('',(-1.164167045979,-10.9435285676,
|
||||
11.788067939419));
|
||||
#192 = CARTESIAN_POINT('',(-0.901928016914,-10.96650203127,
|
||||
11.875311066446));
|
||||
#193 = CARTESIAN_POINT('',(-0.371875127732,-10.99660615524,
|
||||
11.987977286074));
|
||||
#194 = CARTESIAN_POINT('',(-0.110970775212,-11.00480136742,
|
||||
12.017613390432));
|
||||
#195 = CARTESIAN_POINT('',(0.154044903682,-11.00581158676,
|
||||
12.020939532078));
|
||||
#196 = CARTESIAN_POINT('',(0.417899218976,-10.99920605305,
|
||||
11.997223641124));
|
||||
#197 = CARTESIAN_POINT('',(0.675301956574,-10.98540660424,
|
||||
11.947326541564));
|
||||
#198 = CARTESIAN_POINT('',(0.921256391971,-10.96563060757,
|
||||
11.873683621587));
|
||||
#199 = CARTESIAN_POINT('',(1.151344483982,-10.9416388314,11.780339397179
|
||||
));
|
||||
#200 = CARTESIAN_POINT('',(1.615331728924,-10.88374449378,
|
||||
11.543926087641));
|
||||
#201 = CARTESIAN_POINT('',(1.844790555774,-10.84828787337,
|
||||
11.392441050153));
|
||||
#202 = CARTESIAN_POINT('',(2.050308680927,-10.81116823604,11.22331735385
|
||||
));
|
||||
#203 = CARTESIAN_POINT('',(2.232359248194,-10.77424422358,
|
||||
11.040306652498));
|
||||
#204 = CARTESIAN_POINT('',(2.391662754536,-10.73898686411,
|
||||
10.846382998433));
|
||||
#205 = CARTESIAN_POINT('',(2.529009637688,-10.70651764133,
|
||||
10.643900023181));
|
||||
#206 = CARTESIAN_POINT('',(2.645138305672,-10.67768546127,
|
||||
10.434702100122));
|
||||
#207 = CARTESIAN_POINT('',(2.85530473354,-10.62361774287,9.962660574488)
|
||||
);
|
||||
#208 = CARTESIAN_POINT('',(2.940294167414,-10.60024937474,9.697330811818
|
||||
));
|
||||
#209 = CARTESIAN_POINT('',(2.996436520884,-10.58414357274,9.426929463329
|
||||
));
|
||||
#210 = CARTESIAN_POINT('',(3.024268131702,-10.575989631,9.153706681797)
|
||||
);
|
||||
#211 = CARTESIAN_POINT('',(3.024041649892,-10.57609184962,8.87967057245)
|
||||
);
|
||||
#212 = CARTESIAN_POINT('',(2.995725631373,-10.58439583235,8.606771307622
|
||||
));
|
||||
#213 = CARTESIAN_POINT('',(2.938989259398,-10.60048293223,8.337074927084
|
||||
));
|
||||
#214 = CARTESIAN_POINT('',(2.77460924759,-10.64463059066,7.831165270903)
|
||||
);
|
||||
#215 = CARTESIAN_POINT('',(2.67175104555,-10.67154326208,7.59427485792)
|
||||
);
|
||||
#216 = CARTESIAN_POINT('',(2.544056330611,-10.70365056993,7.363762211941
|
||||
));
|
||||
#217 = CARTESIAN_POINT('',(2.390660235376,-10.74006998818,7.141595617126
|
||||
));
|
||||
#218 = CARTESIAN_POINT('',(2.210506350817,-10.7795878533,6.930367243669)
|
||||
);
|
||||
#219 = CARTESIAN_POINT('',(2.002505624649,-10.82052918413,6.733473412639
|
||||
));
|
||||
#220 = CARTESIAN_POINT('',(1.76577358126,-10.86066377413,6.555368237331)
|
||||
);
|
||||
#221 = CARTESIAN_POINT('',(1.187250065257,-10.94029721233,6.221357529634
|
||||
));
|
||||
#222 = CARTESIAN_POINT('',(0.823824071924,-10.97986998106,6.068899191573
|
||||
));
|
||||
#223 = CARTESIAN_POINT('',(0.422202059946,-11.00848612598,5.968367559822
|
||||
));
|
||||
#224 = CARTESIAN_POINT('',(2.076561145259E-12,-11.01910836895,
|
||||
5.933413112681));
|
||||
#225 = CARTESIAN_POINT('',(-0.42220205995,-11.00848612598,5.968367559823
|
||||
));
|
||||
#226 = CARTESIAN_POINT('',(-0.823824071923,-10.97986998106,
|
||||
6.068899191572));
|
||||
#227 = CARTESIAN_POINT('',(-1.187250065258,-10.94029721233,
|
||||
6.221357529634));
|
||||
#228 = CARTESIAN_POINT('',(-1.890223596673,-10.84353329916,6.62721948723
|
||||
));
|
||||
#229 = CARTESIAN_POINT('',(-2.218095833563,-10.78212870183,
|
||||
6.905826251681));
|
||||
#230 = CARTESIAN_POINT('',(-2.484538511301,-10.72169165657,
|
||||
7.219322097354));
|
||||
#231 = CARTESIAN_POINT('',(-2.692957757048,-10.66854367312,
|
||||
7.555805901262));
|
||||
#232 = CARTESIAN_POINT('',(-2.846758847749,-10.62644198358,7.90703918817
|
||||
));
|
||||
#233 = CARTESIAN_POINT('',(-2.94864197823,-10.59756449647,8.267356983371
|
||||
));
|
||||
#234 = CARTESIAN_POINT('',(-3.,-10.58300524425,8.632829859132));
|
||||
#235 = CARTESIAN_POINT('',(-3.,-10.58300524425,9.));
|
||||
#236 = CYLINDRICAL_SURFACE('',#237,11.);
|
||||
#237 = AXIS2_PLACEMENT_3D('',#238,#239,#240);
|
||||
#238 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#239 = DIRECTION('',(-0.,-0.,-1.));
|
||||
#240 = DIRECTION('',(1.,0.,0.));
|
||||
#241 = ADVANCED_FACE('',(#242,#245),#264,.F.);
|
||||
#242 = FACE_BOUND('',#243,.F.);
|
||||
#243 = EDGE_LOOP('',(#244));
|
||||
#244 = ORIENTED_EDGE('',*,*,#39,.T.);
|
||||
#245 = FACE_BOUND('',#246,.F.);
|
||||
#246 = EDGE_LOOP('',(#247,#257));
|
||||
#247 = ORIENTED_EDGE('',*,*,#248,.F.);
|
||||
#248 = EDGE_CURVE('',#249,#251,#253,.T.);
|
||||
#249 = VERTEX_POINT('',#250);
|
||||
#250 = CARTESIAN_POINT('',(-1.581929201956,-2.,0.));
|
||||
#251 = VERTEX_POINT('',#252);
|
||||
#252 = CARTESIAN_POINT('',(1.581929201956,-2.,0.));
|
||||
#253 = LINE('',#254,#255);
|
||||
#254 = CARTESIAN_POINT('',(-1.581929201956,-2.,0.));
|
||||
#255 = VECTOR('',#256,1.);
|
||||
#256 = DIRECTION('',(1.,0.,0.));
|
||||
#257 = ORIENTED_EDGE('',*,*,#258,.T.);
|
||||
#258 = EDGE_CURVE('',#249,#251,#259,.T.);
|
||||
#259 = CIRCLE('',#260,2.55);
|
||||
#260 = AXIS2_PLACEMENT_3D('',#261,#262,#263);
|
||||
#261 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#262 = DIRECTION('',(0.,0.,-1.));
|
||||
#263 = DIRECTION('',(1.,0.,0.));
|
||||
#264 = PLANE('',#265);
|
||||
#265 = AXIS2_PLACEMENT_3D('',#266,#267,#268);
|
||||
#266 = CARTESIAN_POINT('',(-7.458764359306E-16,-4.490371596874E-16,0.));
|
||||
#267 = DIRECTION('',(0.,0.,1.));
|
||||
#268 = DIRECTION('',(1.,0.,0.));
|
||||
#269 = ADVANCED_FACE('',(#270),#290,.T.);
|
||||
#270 = FACE_BOUND('',#271,.T.);
|
||||
#271 = EDGE_LOOP('',(#272,#281,#282,#283));
|
||||
#272 = ORIENTED_EDGE('',*,*,#273,.F.);
|
||||
#273 = EDGE_CURVE('',#24,#274,#276,.T.);
|
||||
#274 = VERTEX_POINT('',#275);
|
||||
#275 = CARTESIAN_POINT('',(10.,0.,40.));
|
||||
#276 = CIRCLE('',#277,1.);
|
||||
#277 = AXIS2_PLACEMENT_3D('',#278,#279,#280);
|
||||
#278 = CARTESIAN_POINT('',(10.,0.,39.));
|
||||
#279 = DIRECTION('',(0.,-1.,0.));
|
||||
#280 = DIRECTION('',(1.,0.,0.));
|
||||
#281 = ORIENTED_EDGE('',*,*,#31,.T.);
|
||||
#282 = ORIENTED_EDGE('',*,*,#273,.T.);
|
||||
#283 = ORIENTED_EDGE('',*,*,#284,.F.);
|
||||
#284 = EDGE_CURVE('',#274,#274,#285,.T.);
|
||||
#285 = CIRCLE('',#286,10.);
|
||||
#286 = AXIS2_PLACEMENT_3D('',#287,#288,#289);
|
||||
#287 = CARTESIAN_POINT('',(0.,0.,40.));
|
||||
#288 = DIRECTION('',(0.,0.,1.));
|
||||
#289 = DIRECTION('',(1.,0.,0.));
|
||||
#290 = TOROIDAL_SURFACE('',#291,10.,1.);
|
||||
#291 = AXIS2_PLACEMENT_3D('',#292,#293,#294);
|
||||
#292 = CARTESIAN_POINT('',(0.,0.,39.));
|
||||
#293 = DIRECTION('',(0.,0.,1.));
|
||||
#294 = DIRECTION('',(1.,0.,0.));
|
||||
#295 = ADVANCED_FACE('',(#296),#317,.F.);
|
||||
#296 = FACE_BOUND('',#297,.T.);
|
||||
#297 = EDGE_LOOP('',(#298,#299,#307,#314,#315,#316));
|
||||
#298 = ORIENTED_EDGE('',*,*,#113,.F.);
|
||||
#299 = ORIENTED_EDGE('',*,*,#300,.T.);
|
||||
#300 = EDGE_CURVE('',#114,#301,#303,.T.);
|
||||
#301 = VERTEX_POINT('',#302);
|
||||
#302 = CARTESIAN_POINT('',(-3.,-9.,9.));
|
||||
#303 = LINE('',#304,#305);
|
||||
#304 = CARTESIAN_POINT('',(-3.,-11.,9.));
|
||||
#305 = VECTOR('',#306,1.);
|
||||
#306 = DIRECTION('',(0.,1.,-2.22044604925E-16));
|
||||
#307 = ORIENTED_EDGE('',*,*,#308,.T.);
|
||||
#308 = EDGE_CURVE('',#301,#301,#309,.T.);
|
||||
#309 = CIRCLE('',#310,3.);
|
||||
#310 = AXIS2_PLACEMENT_3D('',#311,#312,#313);
|
||||
#311 = CARTESIAN_POINT('',(0.,-9.,9.));
|
||||
#312 = DIRECTION('',(0.,1.,-2.22044604925E-16));
|
||||
#313 = DIRECTION('',(-1.,0.,0.));
|
||||
#314 = ORIENTED_EDGE('',*,*,#300,.F.);
|
||||
#315 = ORIENTED_EDGE('',*,*,#176,.F.);
|
||||
#316 = ORIENTED_EDGE('',*,*,#48,.F.);
|
||||
#317 = CYLINDRICAL_SURFACE('',#318,3.);
|
||||
#318 = AXIS2_PLACEMENT_3D('',#319,#320,#321);
|
||||
#319 = CARTESIAN_POINT('',(0.,-11.,9.));
|
||||
#320 = DIRECTION('',(0.,-1.,2.22044604925E-16));
|
||||
#321 = DIRECTION('',(-1.,0.,0.));
|
||||
#322 = ADVANCED_FACE('',(#323),#349,.T.);
|
||||
#323 = FACE_BOUND('',#324,.T.);
|
||||
#324 = EDGE_LOOP('',(#325,#326,#334,#343));
|
||||
#325 = ORIENTED_EDGE('',*,*,#248,.F.);
|
||||
#326 = ORIENTED_EDGE('',*,*,#327,.T.);
|
||||
#327 = EDGE_CURVE('',#249,#328,#330,.T.);
|
||||
#328 = VERTEX_POINT('',#329);
|
||||
#329 = CARTESIAN_POINT('',(-1.581929201956,-2.,7.776276174948));
|
||||
#330 = LINE('',#331,#332);
|
||||
#331 = CARTESIAN_POINT('',(-1.581929201956,-2.,0.));
|
||||
#332 = VECTOR('',#333,1.);
|
||||
#333 = DIRECTION('',(0.,0.,1.));
|
||||
#334 = ORIENTED_EDGE('',*,*,#335,.F.);
|
||||
#335 = EDGE_CURVE('',#336,#328,#338,.T.);
|
||||
#336 = VERTEX_POINT('',#337);
|
||||
#337 = CARTESIAN_POINT('',(1.581929201956,-2.,7.776276174948));
|
||||
#338 = CIRCLE('',#339,2.);
|
||||
#339 = AXIS2_PLACEMENT_3D('',#340,#341,#342);
|
||||
#340 = CARTESIAN_POINT('',(0.,-2.,9.));
|
||||
#341 = DIRECTION('',(0.,1.,-2.22044604925E-16));
|
||||
#342 = DIRECTION('',(-1.,0.,0.));
|
||||
#343 = ORIENTED_EDGE('',*,*,#344,.F.);
|
||||
#344 = EDGE_CURVE('',#251,#336,#345,.T.);
|
||||
#345 = LINE('',#346,#347);
|
||||
#346 = CARTESIAN_POINT('',(1.581929201956,-2.,0.));
|
||||
#347 = VECTOR('',#348,1.);
|
||||
#348 = DIRECTION('',(0.,0.,1.));
|
||||
#349 = PLANE('',#350);
|
||||
#350 = AXIS2_PLACEMENT_3D('',#351,#352,#353);
|
||||
#351 = CARTESIAN_POINT('',(-1.581929201956,-2.,0.));
|
||||
#352 = DIRECTION('',(0.,1.,0.));
|
||||
#353 = DIRECTION('',(1.,0.,0.));
|
||||
#354 = ADVANCED_FACE('',(#355),#399,.F.);
|
||||
#355 = FACE_BOUND('',#356,.F.);
|
||||
#356 = EDGE_LOOP('',(#357,#358,#366,#374,#383,#391,#397,#398));
|
||||
#357 = ORIENTED_EDGE('',*,*,#327,.T.);
|
||||
#358 = ORIENTED_EDGE('',*,*,#359,.T.);
|
||||
#359 = EDGE_CURVE('',#328,#360,#362,.T.);
|
||||
#360 = VERTEX_POINT('',#361);
|
||||
#361 = CARTESIAN_POINT('',(-1.581929201956,-2.,10.223723825052));
|
||||
#362 = LINE('',#363,#364);
|
||||
#363 = CARTESIAN_POINT('',(-1.581929201956,-2.,0.));
|
||||
#364 = VECTOR('',#365,1.);
|
||||
#365 = DIRECTION('',(0.,0.,1.));
|
||||
#366 = ORIENTED_EDGE('',*,*,#367,.T.);
|
||||
#367 = EDGE_CURVE('',#360,#368,#370,.T.);
|
||||
#368 = VERTEX_POINT('',#369);
|
||||
#369 = CARTESIAN_POINT('',(-1.581929201956,-2.,18.));
|
||||
#370 = LINE('',#371,#372);
|
||||
#371 = CARTESIAN_POINT('',(-1.581929201956,-2.,0.));
|
||||
#372 = VECTOR('',#373,1.);
|
||||
#373 = DIRECTION('',(0.,0.,1.));
|
||||
#374 = ORIENTED_EDGE('',*,*,#375,.T.);
|
||||
#375 = EDGE_CURVE('',#368,#376,#378,.T.);
|
||||
#376 = VERTEX_POINT('',#377);
|
||||
#377 = CARTESIAN_POINT('',(1.581929201956,-2.,18.));
|
||||
#378 = CIRCLE('',#379,2.55);
|
||||
#379 = AXIS2_PLACEMENT_3D('',#380,#381,#382);
|
||||
#380 = CARTESIAN_POINT('',(0.,0.,18.));
|
||||
#381 = DIRECTION('',(0.,0.,-1.));
|
||||
#382 = DIRECTION('',(1.,0.,0.));
|
||||
#383 = ORIENTED_EDGE('',*,*,#384,.F.);
|
||||
#384 = EDGE_CURVE('',#385,#376,#387,.T.);
|
||||
#385 = VERTEX_POINT('',#386);
|
||||
#386 = CARTESIAN_POINT('',(1.581929201956,-2.,10.223723825052));
|
||||
#387 = LINE('',#388,#389);
|
||||
#388 = CARTESIAN_POINT('',(1.581929201956,-2.,0.));
|
||||
#389 = VECTOR('',#390,1.);
|
||||
#390 = DIRECTION('',(0.,0.,1.));
|
||||
#391 = ORIENTED_EDGE('',*,*,#392,.F.);
|
||||
#392 = EDGE_CURVE('',#336,#385,#393,.T.);
|
||||
#393 = LINE('',#394,#395);
|
||||
#394 = CARTESIAN_POINT('',(1.581929201956,-2.,0.));
|
||||
#395 = VECTOR('',#396,1.);
|
||||
#396 = DIRECTION('',(0.,0.,1.));
|
||||
#397 = ORIENTED_EDGE('',*,*,#344,.F.);
|
||||
#398 = ORIENTED_EDGE('',*,*,#258,.F.);
|
||||
#399 = CYLINDRICAL_SURFACE('',#400,2.55);
|
||||
#400 = AXIS2_PLACEMENT_3D('',#401,#402,#403);
|
||||
#401 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#402 = DIRECTION('',(0.,0.,-1.));
|
||||
#403 = DIRECTION('',(1.,0.,0.));
|
||||
#404 = ADVANCED_FACE('',(#405,#408),#419,.T.);
|
||||
#405 = FACE_BOUND('',#406,.T.);
|
||||
#406 = EDGE_LOOP('',(#407));
|
||||
#407 = ORIENTED_EDGE('',*,*,#284,.T.);
|
||||
#408 = FACE_BOUND('',#409,.T.);
|
||||
#409 = EDGE_LOOP('',(#410));
|
||||
#410 = ORIENTED_EDGE('',*,*,#411,.T.);
|
||||
#411 = EDGE_CURVE('',#412,#412,#414,.T.);
|
||||
#412 = VERTEX_POINT('',#413);
|
||||
#413 = CARTESIAN_POINT('',(0.,-9.916515138991,40.));
|
||||
#414 = CIRCLE('',#415,9.916515138991);
|
||||
#415 = AXIS2_PLACEMENT_3D('',#416,#417,#418);
|
||||
#416 = CARTESIAN_POINT('',(0.,0.,40.));
|
||||
#417 = DIRECTION('',(-0.,-0.,-1.));
|
||||
#418 = DIRECTION('',(0.,-1.,0.));
|
||||
#419 = PLANE('',#420);
|
||||
#420 = AXIS2_PLACEMENT_3D('',#421,#422,#423);
|
||||
#421 = CARTESIAN_POINT('',(-7.458764359306E-16,-4.490371596874E-16,40.)
|
||||
);
|
||||
#422 = DIRECTION('',(0.,0.,1.));
|
||||
#423 = DIRECTION('',(1.,0.,0.));
|
||||
#424 = ADVANCED_FACE('',(#425,#428),#439,.F.);
|
||||
#425 = FACE_BOUND('',#426,.F.);
|
||||
#426 = EDGE_LOOP('',(#427));
|
||||
#427 = ORIENTED_EDGE('',*,*,#308,.T.);
|
||||
#428 = FACE_BOUND('',#429,.F.);
|
||||
#429 = EDGE_LOOP('',(#430));
|
||||
#430 = ORIENTED_EDGE('',*,*,#431,.F.);
|
||||
#431 = EDGE_CURVE('',#432,#432,#434,.T.);
|
||||
#432 = VERTEX_POINT('',#433);
|
||||
#433 = CARTESIAN_POINT('',(-2.,-9.,9.));
|
||||
#434 = CIRCLE('',#435,2.);
|
||||
#435 = AXIS2_PLACEMENT_3D('',#436,#437,#438);
|
||||
#436 = CARTESIAN_POINT('',(0.,-9.,9.));
|
||||
#437 = DIRECTION('',(0.,1.,-2.22044604925E-16));
|
||||
#438 = DIRECTION('',(-1.,0.,0.));
|
||||
#439 = PLANE('',#440);
|
||||
#440 = AXIS2_PLACEMENT_3D('',#441,#442,#443);
|
||||
#441 = CARTESIAN_POINT('',(2.279066887566E-16,-9.,9.));
|
||||
#442 = DIRECTION('',(0.,1.,0.));
|
||||
#443 = DIRECTION('',(0.,0.,1.));
|
||||
#444 = ADVANCED_FACE('',(#445),#486,.F.);
|
||||
#445 = FACE_BOUND('',#446,.F.);
|
||||
#446 = EDGE_LOOP('',(#447,#455,#462,#463,#470,#477,#484,#485));
|
||||
#447 = ORIENTED_EDGE('',*,*,#448,.F.);
|
||||
#448 = EDGE_CURVE('',#449,#432,#451,.T.);
|
||||
#449 = VERTEX_POINT('',#450);
|
||||
#450 = CARTESIAN_POINT('',(-2.,-2.,9.));
|
||||
#451 = LINE('',#452,#453);
|
||||
#452 = CARTESIAN_POINT('',(-2.,-2.,9.));
|
||||
#453 = VECTOR('',#454,1.);
|
||||
#454 = DIRECTION('',(0.,-1.,2.22044604925E-16));
|
||||
#455 = ORIENTED_EDGE('',*,*,#456,.F.);
|
||||
#456 = EDGE_CURVE('',#328,#449,#457,.T.);
|
||||
#457 = CIRCLE('',#458,2.);
|
||||
#458 = AXIS2_PLACEMENT_3D('',#459,#460,#461);
|
||||
#459 = CARTESIAN_POINT('',(0.,-2.,9.));
|
||||
#460 = DIRECTION('',(0.,1.,-2.22044604925E-16));
|
||||
#461 = DIRECTION('',(-1.,0.,0.));
|
||||
#462 = ORIENTED_EDGE('',*,*,#335,.F.);
|
||||
#463 = ORIENTED_EDGE('',*,*,#464,.F.);
|
||||
#464 = EDGE_CURVE('',#385,#336,#465,.T.);
|
||||
#465 = CIRCLE('',#466,2.);
|
||||
#466 = AXIS2_PLACEMENT_3D('',#467,#468,#469);
|
||||
#467 = CARTESIAN_POINT('',(0.,-2.,9.));
|
||||
#468 = DIRECTION('',(0.,1.,-2.22044604925E-16));
|
||||
#469 = DIRECTION('',(-1.,0.,0.));
|
||||
#470 = ORIENTED_EDGE('',*,*,#471,.F.);
|
||||
#471 = EDGE_CURVE('',#360,#385,#472,.T.);
|
||||
#472 = CIRCLE('',#473,2.);
|
||||
#473 = AXIS2_PLACEMENT_3D('',#474,#475,#476);
|
||||
#474 = CARTESIAN_POINT('',(0.,-2.,9.));
|
||||
#475 = DIRECTION('',(0.,1.,-2.22044604925E-16));
|
||||
#476 = DIRECTION('',(-1.,0.,0.));
|
||||
#477 = ORIENTED_EDGE('',*,*,#478,.F.);
|
||||
#478 = EDGE_CURVE('',#449,#360,#479,.T.);
|
||||
#479 = CIRCLE('',#480,2.);
|
||||
#480 = AXIS2_PLACEMENT_3D('',#481,#482,#483);
|
||||
#481 = CARTESIAN_POINT('',(0.,-2.,9.));
|
||||
#482 = DIRECTION('',(0.,1.,-2.22044604925E-16));
|
||||
#483 = DIRECTION('',(-1.,0.,0.));
|
||||
#484 = ORIENTED_EDGE('',*,*,#448,.T.);
|
||||
#485 = ORIENTED_EDGE('',*,*,#431,.T.);
|
||||
#486 = CYLINDRICAL_SURFACE('',#487,2.);
|
||||
#487 = AXIS2_PLACEMENT_3D('',#488,#489,#490);
|
||||
#488 = CARTESIAN_POINT('',(0.,-2.,9.));
|
||||
#489 = DIRECTION('',(0.,1.,-2.22044604925E-16));
|
||||
#490 = DIRECTION('',(-1.,0.,0.));
|
||||
#491 = ADVANCED_FACE('',(#492),#497,.F.);
|
||||
#492 = FACE_BOUND('',#493,.F.);
|
||||
#493 = EDGE_LOOP('',(#494,#495,#496));
|
||||
#494 = ORIENTED_EDGE('',*,*,#456,.T.);
|
||||
#495 = ORIENTED_EDGE('',*,*,#478,.T.);
|
||||
#496 = ORIENTED_EDGE('',*,*,#359,.F.);
|
||||
#497 = PLANE('',#498);
|
||||
#498 = AXIS2_PLACEMENT_3D('',#499,#500,#501);
|
||||
#499 = CARTESIAN_POINT('',(4.273250414186E-17,-2.,9.));
|
||||
#500 = DIRECTION('',(0.,1.,-2.563950248511E-16));
|
||||
#501 = DIRECTION('',(0.,2.563950248511E-16,1.));
|
||||
#502 = ADVANCED_FACE('',(#503),#514,.T.);
|
||||
#503 = FACE_BOUND('',#504,.T.);
|
||||
#504 = EDGE_LOOP('',(#505,#506,#507,#513));
|
||||
#505 = ORIENTED_EDGE('',*,*,#471,.F.);
|
||||
#506 = ORIENTED_EDGE('',*,*,#367,.T.);
|
||||
#507 = ORIENTED_EDGE('',*,*,#508,.T.);
|
||||
#508 = EDGE_CURVE('',#368,#376,#509,.T.);
|
||||
#509 = LINE('',#510,#511);
|
||||
#510 = CARTESIAN_POINT('',(-1.581929201956,-2.,18.));
|
||||
#511 = VECTOR('',#512,1.);
|
||||
#512 = DIRECTION('',(1.,0.,0.));
|
||||
#513 = ORIENTED_EDGE('',*,*,#384,.F.);
|
||||
#514 = PLANE('',#515);
|
||||
#515 = AXIS2_PLACEMENT_3D('',#516,#517,#518);
|
||||
#516 = CARTESIAN_POINT('',(-1.581929201956,-2.,0.));
|
||||
#517 = DIRECTION('',(0.,1.,0.));
|
||||
#518 = DIRECTION('',(1.,0.,0.));
|
||||
#519 = ADVANCED_FACE('',(#520),#524,.F.);
|
||||
#520 = FACE_BOUND('',#521,.F.);
|
||||
#521 = EDGE_LOOP('',(#522,#523));
|
||||
#522 = ORIENTED_EDGE('',*,*,#392,.T.);
|
||||
#523 = ORIENTED_EDGE('',*,*,#464,.T.);
|
||||
#524 = PLANE('',#525);
|
||||
#525 = AXIS2_PLACEMENT_3D('',#526,#527,#528);
|
||||
#526 = CARTESIAN_POINT('',(4.273250414186E-17,-2.,9.));
|
||||
#527 = DIRECTION('',(0.,1.,-2.563950248511E-16));
|
||||
#528 = DIRECTION('',(0.,2.563950248511E-16,1.));
|
||||
#529 = ADVANCED_FACE('',(#530),#534,.F.);
|
||||
#530 = FACE_BOUND('',#531,.T.);
|
||||
#531 = EDGE_LOOP('',(#532,#533));
|
||||
#532 = ORIENTED_EDGE('',*,*,#375,.T.);
|
||||
#533 = ORIENTED_EDGE('',*,*,#508,.F.);
|
||||
#534 = PLANE('',#535);
|
||||
#535 = AXIS2_PLACEMENT_3D('',#536,#537,#538);
|
||||
#536 = CARTESIAN_POINT('',(2.776832757453E-16,-5.892051345584E-02,18.));
|
||||
#537 = DIRECTION('',(0.,0.,1.));
|
||||
#538 = DIRECTION('',(1.,0.,0.));
|
||||
#539 = ADVANCED_FACE('',(#540),#560,.F.);
|
||||
#540 = FACE_BOUND('',#541,.F.);
|
||||
#541 = EDGE_LOOP('',(#542,#551,#558,#559));
|
||||
#542 = ORIENTED_EDGE('',*,*,#543,.T.);
|
||||
#543 = EDGE_CURVE('',#412,#544,#546,.T.);
|
||||
#544 = VERTEX_POINT('',#545);
|
||||
#545 = CARTESIAN_POINT('',(0.,-8.,39.6));
|
||||
#546 = CIRCLE('',#547,1.);
|
||||
#547 = AXIS2_PLACEMENT_3D('',#548,#549,#550);
|
||||
#548 = CARTESIAN_POINT('',(0.,-9.,39.6));
|
||||
#549 = DIRECTION('',(1.,0.,0.));
|
||||
#550 = DIRECTION('',(0.,1.,0.));
|
||||
#551 = ORIENTED_EDGE('',*,*,#552,.F.);
|
||||
#552 = EDGE_CURVE('',#544,#544,#553,.T.);
|
||||
#553 = CIRCLE('',#554,8.);
|
||||
#554 = AXIS2_PLACEMENT_3D('',#555,#556,#557);
|
||||
#555 = CARTESIAN_POINT('',(0.,0.,39.6));
|
||||
#556 = DIRECTION('',(-0.,-0.,-1.));
|
||||
#557 = DIRECTION('',(0.,-1.,0.));
|
||||
#558 = ORIENTED_EDGE('',*,*,#543,.F.);
|
||||
#559 = ORIENTED_EDGE('',*,*,#411,.T.);
|
||||
#560 = TOROIDAL_SURFACE('',#561,9.,1.);
|
||||
#561 = AXIS2_PLACEMENT_3D('',#562,#563,#564);
|
||||
#562 = CARTESIAN_POINT('',(0.,0.,39.6));
|
||||
#563 = DIRECTION('',(-0.,-0.,-1.));
|
||||
#564 = DIRECTION('',(0.,-1.,0.));
|
||||
#565 = ADVANCED_FACE('',(#566),#586,.F.);
|
||||
#566 = FACE_BOUND('',#567,.F.);
|
||||
#567 = EDGE_LOOP('',(#568,#577,#578,#579));
|
||||
#568 = ORIENTED_EDGE('',*,*,#569,.F.);
|
||||
#569 = EDGE_CURVE('',#544,#570,#572,.T.);
|
||||
#570 = VERTEX_POINT('',#571);
|
||||
#571 = CARTESIAN_POINT('',(0.,-1.502502139544E-03,33.6));
|
||||
#572 = ELLIPSE('',#573,8.,6.000000105821);
|
||||
#573 = AXIS2_PLACEMENT_3D('',#574,#575,#576);
|
||||
#574 = CARTESIAN_POINT('',(0.,4.561730651331E-33,39.6));
|
||||
#575 = DIRECTION('',(1.,0.,0.));
|
||||
#576 = DIRECTION('',(0.,-1.,0.));
|
||||
#577 = ORIENTED_EDGE('',*,*,#552,.T.);
|
||||
#578 = ORIENTED_EDGE('',*,*,#569,.T.);
|
||||
#579 = ORIENTED_EDGE('',*,*,#580,.F.);
|
||||
#580 = EDGE_CURVE('',#570,#570,#581,.T.);
|
||||
#581 = CIRCLE('',#582,1.502502139544E-03);
|
||||
#582 = AXIS2_PLACEMENT_3D('',#583,#584,#585);
|
||||
#583 = CARTESIAN_POINT('',(0.,0.,33.6));
|
||||
#584 = DIRECTION('',(-0.,-0.,-1.));
|
||||
#585 = DIRECTION('',(0.,-1.,0.));
|
||||
#586 = SURFACE_OF_REVOLUTION('',#587,#592);
|
||||
#587 = ELLIPSE('',#588,8.,6.000000105821);
|
||||
#588 = AXIS2_PLACEMENT_3D('',#589,#590,#591);
|
||||
#589 = CARTESIAN_POINT('',(0.,4.561730651331E-33,39.6));
|
||||
#590 = DIRECTION('',(1.,0.,0.));
|
||||
#591 = DIRECTION('',(0.,-1.,0.));
|
||||
#592 = AXIS1_PLACEMENT('',#593,#594);
|
||||
#593 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#594 = DIRECTION('',(-0.,-0.,-1.));
|
||||
#595 = ADVANCED_FACE('',(#596),#615,.T.);
|
||||
#596 = FACE_BOUND('',#597,.T.);
|
||||
#597 = EDGE_LOOP('',(#598,#607,#613,#614));
|
||||
#598 = ORIENTED_EDGE('',*,*,#599,.T.);
|
||||
#599 = EDGE_CURVE('',#600,#600,#602,.T.);
|
||||
#600 = VERTEX_POINT('',#601);
|
||||
#601 = CARTESIAN_POINT('',(0.,-1.502502139542E-03,40.));
|
||||
#602 = CIRCLE('',#603,1.502502139542E-03);
|
||||
#603 = AXIS2_PLACEMENT_3D('',#604,#605,#606);
|
||||
#604 = CARTESIAN_POINT('',(0.,0.,40.));
|
||||
#605 = DIRECTION('',(-0.,-0.,-1.));
|
||||
#606 = DIRECTION('',(0.,-1.,0.));
|
||||
#607 = ORIENTED_EDGE('',*,*,#608,.T.);
|
||||
#608 = EDGE_CURVE('',#600,#570,#609,.T.);
|
||||
#609 = LINE('',#610,#611);
|
||||
#610 = CARTESIAN_POINT('',(0.,-1.502502139542E-03,45.6));
|
||||
#611 = VECTOR('',#612,1.);
|
||||
#612 = DIRECTION('',(0.,0.,-1.));
|
||||
#613 = ORIENTED_EDGE('',*,*,#580,.F.);
|
||||
#614 = ORIENTED_EDGE('',*,*,#608,.F.);
|
||||
#615 = CYLINDRICAL_SURFACE('',#616,1.502502139542E-03);
|
||||
#616 = AXIS2_PLACEMENT_3D('',#617,#618,#619);
|
||||
#617 = CARTESIAN_POINT('',(0.,0.,45.6));
|
||||
#618 = DIRECTION('',(-0.,-0.,-1.));
|
||||
#619 = DIRECTION('',(0.,-1.,0.));
|
||||
#620 = ADVANCED_FACE('',(#621),#624,.T.);
|
||||
#621 = FACE_BOUND('',#622,.T.);
|
||||
#622 = EDGE_LOOP('',(#623));
|
||||
#623 = ORIENTED_EDGE('',*,*,#599,.F.);
|
||||
#624 = PLANE('',#625);
|
||||
#625 = AXIS2_PLACEMENT_3D('',#626,#627,#628);
|
||||
#626 = CARTESIAN_POINT('',(-7.458764359306E-16,-4.490371596874E-16,40.)
|
||||
);
|
||||
#627 = DIRECTION('',(0.,0.,1.));
|
||||
#628 = DIRECTION('',(1.,0.,0.));
|
||||
#629 = ( GEOMETRIC_REPRESENTATION_CONTEXT(3)
|
||||
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#633)) GLOBAL_UNIT_ASSIGNED_CONTEXT
|
||||
((#630,#631,#632)) REPRESENTATION_CONTEXT('Context #1',
|
||||
'3D Context with UNIT and UNCERTAINTY') );
|
||||
#630 = ( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.) );
|
||||
#631 = ( NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.) );
|
||||
#632 = ( NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT() );
|
||||
#633 = UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.E-07),#630,
|
||||
'distance_accuracy_value','confusion accuracy');
|
||||
#634 = PRODUCT_RELATED_PRODUCT_CATEGORY('part',$,(#7));
|
||||
#635 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION('',(#636)
|
||||
,#629);
|
||||
#636 = STYLED_ITEM('color',(#637),#15);
|
||||
#637 = PRESENTATION_STYLE_ASSIGNMENT((#638,#644));
|
||||
#638 = SURFACE_STYLE_USAGE(.BOTH.,#639);
|
||||
#639 = SURFACE_SIDE_STYLE('',(#640));
|
||||
#640 = SURFACE_STYLE_FILL_AREA(#641);
|
||||
#641 = FILL_AREA_STYLE('',(#642));
|
||||
#642 = FILL_AREA_STYLE_COLOUR('',#643);
|
||||
#643 = COLOUR_RGB('',0.668813114255,0.490523995954,0.661122442898);
|
||||
#644 = CURVE_STYLE('',#645,POSITIVE_LENGTH_MEASURE(0.1),#646);
|
||||
#645 = DRAUGHTING_PRE_DEFINED_CURVE_FONT('continuous');
|
||||
#646 = COLOUR_RGB('',9.803921802644E-02,9.803921802644E-02,
|
||||
9.803921802644E-02);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
46325
CAD/Zusatzteile-EiAndockerLinksTPU.obj
Normal file
46325
CAD/Zusatzteile-EiAndockerLinksTPU.obj
Normal file
File diff suppressed because it is too large
Load Diff
608
CAD/Zusatzteile-EiAndockerRechtsORing16x2.step
Normal file
608
CAD/Zusatzteile-EiAndockerRechtsORing16x2.step
Normal file
@@ -0,0 +1,608 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('FreeCAD Model'),'2;1');
|
||||
FILE_NAME('Open CASCADE Shape Model','2026-03-20T00:36:18',('Author'),(
|
||||
''),'Open CASCADE STEP processor 7.8','FreeCAD','Unknown');
|
||||
FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1 = APPLICATION_PROTOCOL_DEFINITION('international standard',
|
||||
'automotive_design',2000,#2);
|
||||
#2 = APPLICATION_CONTEXT(
|
||||
'core data for automotive mechanical design processes');
|
||||
#3 = SHAPE_DEFINITION_REPRESENTATION(#4,#10);
|
||||
#4 = PRODUCT_DEFINITION_SHAPE('','',#5);
|
||||
#5 = PRODUCT_DEFINITION('design','',#6,#9);
|
||||
#6 = PRODUCT_DEFINITION_FORMATION('','',#7);
|
||||
#7 = PRODUCT('EiAndockerRechtsORing16x2','EiAndockerRechtsORing16x2','',
|
||||
(#8));
|
||||
#8 = PRODUCT_CONTEXT('',#2,'mechanical');
|
||||
#9 = PRODUCT_DEFINITION_CONTEXT('part definition',#2,'design');
|
||||
#10 = ADVANCED_BREP_SHAPE_REPRESENTATION('',(#11,#15),#548);
|
||||
#11 = AXIS2_PLACEMENT_3D('',#12,#13,#14);
|
||||
#12 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#13 = DIRECTION('',(0.,0.,1.));
|
||||
#14 = DIRECTION('',(1.,0.,-0.));
|
||||
#15 = MANIFOLD_SOLID_BREP('',#16);
|
||||
#16 = CLOSED_SHELL('',(#17,#50,#76,#102,#122,#142,#168,#193,#229,#254,
|
||||
#437,#457,#488,#513,#539));
|
||||
#17 = ADVANCED_FACE('',(#18),#45,.T.);
|
||||
#18 = FACE_BOUND('',#19,.F.);
|
||||
#19 = EDGE_LOOP('',(#20,#30,#37,#38));
|
||||
#20 = ORIENTED_EDGE('',*,*,#21,.T.);
|
||||
#21 = EDGE_CURVE('',#22,#24,#26,.T.);
|
||||
#22 = VERTEX_POINT('',#23);
|
||||
#23 = CARTESIAN_POINT('',(12.,0.,1.9));
|
||||
#24 = VERTEX_POINT('',#25);
|
||||
#25 = CARTESIAN_POINT('',(12.,0.,7.));
|
||||
#26 = LINE('',#27,#28);
|
||||
#27 = CARTESIAN_POINT('',(12.,0.,0.));
|
||||
#28 = VECTOR('',#29,1.);
|
||||
#29 = DIRECTION('',(0.,0.,1.));
|
||||
#30 = ORIENTED_EDGE('',*,*,#31,.T.);
|
||||
#31 = EDGE_CURVE('',#24,#24,#32,.T.);
|
||||
#32 = CIRCLE('',#33,12.);
|
||||
#33 = AXIS2_PLACEMENT_3D('',#34,#35,#36);
|
||||
#34 = CARTESIAN_POINT('',(0.,0.,7.));
|
||||
#35 = DIRECTION('',(0.,0.,1.));
|
||||
#36 = DIRECTION('',(1.,0.,0.));
|
||||
#37 = ORIENTED_EDGE('',*,*,#21,.F.);
|
||||
#38 = ORIENTED_EDGE('',*,*,#39,.F.);
|
||||
#39 = EDGE_CURVE('',#22,#22,#40,.T.);
|
||||
#40 = CIRCLE('',#41,12.);
|
||||
#41 = AXIS2_PLACEMENT_3D('',#42,#43,#44);
|
||||
#42 = CARTESIAN_POINT('',(0.,0.,1.9));
|
||||
#43 = DIRECTION('',(0.,0.,1.));
|
||||
#44 = DIRECTION('',(1.,0.,-0.));
|
||||
#45 = CYLINDRICAL_SURFACE('',#46,12.);
|
||||
#46 = AXIS2_PLACEMENT_3D('',#47,#48,#49);
|
||||
#47 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#48 = DIRECTION('',(-0.,-0.,-1.));
|
||||
#49 = DIRECTION('',(1.,0.,0.));
|
||||
#50 = ADVANCED_FACE('',(#51),#71,.T.);
|
||||
#51 = FACE_BOUND('',#52,.F.);
|
||||
#52 = EDGE_LOOP('',(#53,#62,#63,#64));
|
||||
#53 = ORIENTED_EDGE('',*,*,#54,.F.);
|
||||
#54 = EDGE_CURVE('',#22,#55,#57,.T.);
|
||||
#55 = VERTEX_POINT('',#56);
|
||||
#56 = CARTESIAN_POINT('',(10.1,0.,0.));
|
||||
#57 = CIRCLE('',#58,1.9);
|
||||
#58 = AXIS2_PLACEMENT_3D('',#59,#60,#61);
|
||||
#59 = CARTESIAN_POINT('',(10.1,0.,1.9));
|
||||
#60 = DIRECTION('',(-0.,1.,0.));
|
||||
#61 = DIRECTION('',(1.,0.,0.));
|
||||
#62 = ORIENTED_EDGE('',*,*,#39,.F.);
|
||||
#63 = ORIENTED_EDGE('',*,*,#54,.T.);
|
||||
#64 = ORIENTED_EDGE('',*,*,#65,.T.);
|
||||
#65 = EDGE_CURVE('',#55,#55,#66,.T.);
|
||||
#66 = CIRCLE('',#67,10.1);
|
||||
#67 = AXIS2_PLACEMENT_3D('',#68,#69,#70);
|
||||
#68 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#69 = DIRECTION('',(0.,0.,1.));
|
||||
#70 = DIRECTION('',(1.,0.,-0.));
|
||||
#71 = TOROIDAL_SURFACE('',#72,10.1,1.9);
|
||||
#72 = AXIS2_PLACEMENT_3D('',#73,#74,#75);
|
||||
#73 = CARTESIAN_POINT('',(0.,0.,1.9));
|
||||
#74 = DIRECTION('',(-0.,-0.,-1.));
|
||||
#75 = DIRECTION('',(1.,0.,0.));
|
||||
#76 = ADVANCED_FACE('',(#77),#97,.T.);
|
||||
#77 = FACE_BOUND('',#78,.F.);
|
||||
#78 = EDGE_LOOP('',(#79,#88,#95,#96));
|
||||
#79 = ORIENTED_EDGE('',*,*,#80,.F.);
|
||||
#80 = EDGE_CURVE('',#81,#24,#83,.T.);
|
||||
#81 = VERTEX_POINT('',#82);
|
||||
#82 = CARTESIAN_POINT('',(10.,0.,9.));
|
||||
#83 = CIRCLE('',#84,2.);
|
||||
#84 = AXIS2_PLACEMENT_3D('',#85,#86,#87);
|
||||
#85 = CARTESIAN_POINT('',(10.,0.,7.));
|
||||
#86 = DIRECTION('',(-0.,1.,0.));
|
||||
#87 = DIRECTION('',(1.,0.,0.));
|
||||
#88 = ORIENTED_EDGE('',*,*,#89,.F.);
|
||||
#89 = EDGE_CURVE('',#81,#81,#90,.T.);
|
||||
#90 = CIRCLE('',#91,10.);
|
||||
#91 = AXIS2_PLACEMENT_3D('',#92,#93,#94);
|
||||
#92 = CARTESIAN_POINT('',(0.,0.,9.));
|
||||
#93 = DIRECTION('',(0.,0.,1.));
|
||||
#94 = DIRECTION('',(1.,0.,0.));
|
||||
#95 = ORIENTED_EDGE('',*,*,#80,.T.);
|
||||
#96 = ORIENTED_EDGE('',*,*,#31,.T.);
|
||||
#97 = TOROIDAL_SURFACE('',#98,10.,2.);
|
||||
#98 = AXIS2_PLACEMENT_3D('',#99,#100,#101);
|
||||
#99 = CARTESIAN_POINT('',(0.,0.,7.));
|
||||
#100 = DIRECTION('',(-0.,-0.,-1.));
|
||||
#101 = DIRECTION('',(1.,0.,0.));
|
||||
#102 = ADVANCED_FACE('',(#103,#106),#117,.F.);
|
||||
#103 = FACE_BOUND('',#104,.F.);
|
||||
#104 = EDGE_LOOP('',(#105));
|
||||
#105 = ORIENTED_EDGE('',*,*,#65,.T.);
|
||||
#106 = FACE_BOUND('',#107,.F.);
|
||||
#107 = EDGE_LOOP('',(#108));
|
||||
#108 = ORIENTED_EDGE('',*,*,#109,.F.);
|
||||
#109 = EDGE_CURVE('',#110,#110,#112,.T.);
|
||||
#110 = VERTEX_POINT('',#111);
|
||||
#111 = CARTESIAN_POINT('',(0.,9.916515138991,0.));
|
||||
#112 = CIRCLE('',#113,9.916515138991);
|
||||
#113 = AXIS2_PLACEMENT_3D('',#114,#115,#116);
|
||||
#114 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#115 = DIRECTION('',(0.,-0.,1.));
|
||||
#116 = DIRECTION('',(0.,1.,0.));
|
||||
#117 = PLANE('',#118);
|
||||
#118 = AXIS2_PLACEMENT_3D('',#119,#120,#121);
|
||||
#119 = CARTESIAN_POINT('',(-9.1E-16,-3.E-17,0.));
|
||||
#120 = DIRECTION('',(0.,0.,1.));
|
||||
#121 = DIRECTION('',(1.,0.,0.));
|
||||
#122 = ADVANCED_FACE('',(#123,#126),#137,.T.);
|
||||
#123 = FACE_BOUND('',#124,.T.);
|
||||
#124 = EDGE_LOOP('',(#125));
|
||||
#125 = ORIENTED_EDGE('',*,*,#89,.T.);
|
||||
#126 = FACE_BOUND('',#127,.T.);
|
||||
#127 = EDGE_LOOP('',(#128));
|
||||
#128 = ORIENTED_EDGE('',*,*,#129,.F.);
|
||||
#129 = EDGE_CURVE('',#130,#130,#132,.T.);
|
||||
#130 = VERTEX_POINT('',#131);
|
||||
#131 = CARTESIAN_POINT('',(7.5,0.,9.));
|
||||
#132 = CIRCLE('',#133,7.5);
|
||||
#133 = AXIS2_PLACEMENT_3D('',#134,#135,#136);
|
||||
#134 = CARTESIAN_POINT('',(0.,0.,9.));
|
||||
#135 = DIRECTION('',(0.,0.,1.));
|
||||
#136 = DIRECTION('',(1.,0.,0.));
|
||||
#137 = PLANE('',#138);
|
||||
#138 = AXIS2_PLACEMENT_3D('',#139,#140,#141);
|
||||
#139 = CARTESIAN_POINT('',(-9.1E-16,-3.E-17,9.));
|
||||
#140 = DIRECTION('',(0.,0.,1.));
|
||||
#141 = DIRECTION('',(1.,0.,0.));
|
||||
#142 = ADVANCED_FACE('',(#143),#163,.F.);
|
||||
#143 = FACE_BOUND('',#144,.F.);
|
||||
#144 = EDGE_LOOP('',(#145,#154,#161,#162));
|
||||
#145 = ORIENTED_EDGE('',*,*,#146,.T.);
|
||||
#146 = EDGE_CURVE('',#110,#147,#149,.T.);
|
||||
#147 = VERTEX_POINT('',#148);
|
||||
#148 = CARTESIAN_POINT('',(0.,10.,0.4));
|
||||
#149 = CIRCLE('',#150,1.);
|
||||
#150 = AXIS2_PLACEMENT_3D('',#151,#152,#153);
|
||||
#151 = CARTESIAN_POINT('',(0.,9.,0.4));
|
||||
#152 = DIRECTION('',(1.,0.,0.));
|
||||
#153 = DIRECTION('',(0.,1.,0.));
|
||||
#154 = ORIENTED_EDGE('',*,*,#155,.F.);
|
||||
#155 = EDGE_CURVE('',#147,#147,#156,.T.);
|
||||
#156 = CIRCLE('',#157,10.);
|
||||
#157 = AXIS2_PLACEMENT_3D('',#158,#159,#160);
|
||||
#158 = CARTESIAN_POINT('',(0.,0.,0.4));
|
||||
#159 = DIRECTION('',(0.,-0.,1.));
|
||||
#160 = DIRECTION('',(0.,1.,0.));
|
||||
#161 = ORIENTED_EDGE('',*,*,#146,.F.);
|
||||
#162 = ORIENTED_EDGE('',*,*,#109,.T.);
|
||||
#163 = TOROIDAL_SURFACE('',#164,9.,1.);
|
||||
#164 = AXIS2_PLACEMENT_3D('',#165,#166,#167);
|
||||
#165 = CARTESIAN_POINT('',(0.,0.,0.4));
|
||||
#166 = DIRECTION('',(0.,0.,1.));
|
||||
#167 = DIRECTION('',(0.,1.,0.));
|
||||
#168 = ADVANCED_FACE('',(#169),#188,.T.);
|
||||
#169 = FACE_BOUND('',#170,.F.);
|
||||
#170 = EDGE_LOOP('',(#171,#179,#186,#187));
|
||||
#171 = ORIENTED_EDGE('',*,*,#172,.T.);
|
||||
#172 = EDGE_CURVE('',#130,#173,#175,.T.);
|
||||
#173 = VERTEX_POINT('',#174);
|
||||
#174 = CARTESIAN_POINT('',(7.5,0.,9.1));
|
||||
#175 = LINE('',#176,#177);
|
||||
#176 = CARTESIAN_POINT('',(7.5,0.,9.));
|
||||
#177 = VECTOR('',#178,1.);
|
||||
#178 = DIRECTION('',(0.,0.,1.));
|
||||
#179 = ORIENTED_EDGE('',*,*,#180,.T.);
|
||||
#180 = EDGE_CURVE('',#173,#173,#181,.T.);
|
||||
#181 = CIRCLE('',#182,7.5);
|
||||
#182 = AXIS2_PLACEMENT_3D('',#183,#184,#185);
|
||||
#183 = CARTESIAN_POINT('',(0.,0.,9.1));
|
||||
#184 = DIRECTION('',(0.,0.,1.));
|
||||
#185 = DIRECTION('',(1.,0.,0.));
|
||||
#186 = ORIENTED_EDGE('',*,*,#172,.F.);
|
||||
#187 = ORIENTED_EDGE('',*,*,#129,.F.);
|
||||
#188 = CYLINDRICAL_SURFACE('',#189,7.5);
|
||||
#189 = AXIS2_PLACEMENT_3D('',#190,#191,#192);
|
||||
#190 = CARTESIAN_POINT('',(0.,0.,9.));
|
||||
#191 = DIRECTION('',(-0.,-0.,-1.));
|
||||
#192 = DIRECTION('',(1.,0.,0.));
|
||||
#193 = ADVANCED_FACE('',(#194),#224,.F.);
|
||||
#194 = FACE_BOUND('',#195,.F.);
|
||||
#195 = EDGE_LOOP('',(#196,#205,#206,#207,#216,#223));
|
||||
#196 = ORIENTED_EDGE('',*,*,#197,.F.);
|
||||
#197 = EDGE_CURVE('',#147,#198,#200,.T.);
|
||||
#198 = VERTEX_POINT('',#199);
|
||||
#199 = CARTESIAN_POINT('',(0.,8.,0.400000001205));
|
||||
#200 = CIRCLE('',#201,1.);
|
||||
#201 = AXIS2_PLACEMENT_3D('',#202,#203,#204);
|
||||
#202 = CARTESIAN_POINT('',(0.,9.,0.4));
|
||||
#203 = DIRECTION('',(1.,0.,0.));
|
||||
#204 = DIRECTION('',(0.,1.,0.));
|
||||
#205 = ORIENTED_EDGE('',*,*,#155,.T.);
|
||||
#206 = ORIENTED_EDGE('',*,*,#197,.T.);
|
||||
#207 = ORIENTED_EDGE('',*,*,#208,.T.);
|
||||
#208 = EDGE_CURVE('',#198,#209,#211,.T.);
|
||||
#209 = VERTEX_POINT('',#210);
|
||||
#210 = CARTESIAN_POINT('',(0.,8.083484861009,0.));
|
||||
#211 = CIRCLE('',#212,1.);
|
||||
#212 = AXIS2_PLACEMENT_3D('',#213,#214,#215);
|
||||
#213 = CARTESIAN_POINT('',(0.,9.,0.4));
|
||||
#214 = DIRECTION('',(1.,0.,0.));
|
||||
#215 = DIRECTION('',(0.,1.,0.));
|
||||
#216 = ORIENTED_EDGE('',*,*,#217,.F.);
|
||||
#217 = EDGE_CURVE('',#209,#209,#218,.T.);
|
||||
#218 = CIRCLE('',#219,8.083484861009);
|
||||
#219 = AXIS2_PLACEMENT_3D('',#220,#221,#222);
|
||||
#220 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#221 = DIRECTION('',(0.,-0.,1.));
|
||||
#222 = DIRECTION('',(0.,1.,0.));
|
||||
#223 = ORIENTED_EDGE('',*,*,#208,.F.);
|
||||
#224 = TOROIDAL_SURFACE('',#225,9.,1.);
|
||||
#225 = AXIS2_PLACEMENT_3D('',#226,#227,#228);
|
||||
#226 = CARTESIAN_POINT('',(0.,0.,0.4));
|
||||
#227 = DIRECTION('',(0.,0.,1.));
|
||||
#228 = DIRECTION('',(0.,1.,0.));
|
||||
#229 = ADVANCED_FACE('',(#230),#249,.T.);
|
||||
#230 = FACE_BOUND('',#231,.F.);
|
||||
#231 = EDGE_LOOP('',(#232,#240,#247,#248));
|
||||
#232 = ORIENTED_EDGE('',*,*,#233,.F.);
|
||||
#233 = EDGE_CURVE('',#234,#173,#236,.T.);
|
||||
#234 = VERTEX_POINT('',#235);
|
||||
#235 = CARTESIAN_POINT('',(6.,0.,10.6));
|
||||
#236 = LINE('',#237,#238);
|
||||
#237 = CARTESIAN_POINT('',(7.5,0.,9.1));
|
||||
#238 = VECTOR('',#239,1.);
|
||||
#239 = DIRECTION('',(0.707106781187,0.,-0.707106781187));
|
||||
#240 = ORIENTED_EDGE('',*,*,#241,.T.);
|
||||
#241 = EDGE_CURVE('',#234,#234,#242,.T.);
|
||||
#242 = CIRCLE('',#243,6.);
|
||||
#243 = AXIS2_PLACEMENT_3D('',#244,#245,#246);
|
||||
#244 = CARTESIAN_POINT('',(0.,0.,10.6));
|
||||
#245 = DIRECTION('',(0.,0.,1.));
|
||||
#246 = DIRECTION('',(1.,0.,0.));
|
||||
#247 = ORIENTED_EDGE('',*,*,#233,.T.);
|
||||
#248 = ORIENTED_EDGE('',*,*,#180,.F.);
|
||||
#249 = CONICAL_SURFACE('',#250,7.5,0.785398163397);
|
||||
#250 = AXIS2_PLACEMENT_3D('',#251,#252,#253);
|
||||
#251 = CARTESIAN_POINT('',(0.,0.,9.1));
|
||||
#252 = DIRECTION('',(-0.,-0.,-1.));
|
||||
#253 = DIRECTION('',(1.,0.,0.));
|
||||
#254 = ADVANCED_FACE('',(#255,#258),#432,.F.);
|
||||
#255 = FACE_BOUND('',#256,.F.);
|
||||
#256 = EDGE_LOOP('',(#257));
|
||||
#257 = ORIENTED_EDGE('',*,*,#217,.T.);
|
||||
#258 = FACE_BOUND('',#259,.F.);
|
||||
#259 = EDGE_LOOP('',(#260));
|
||||
#260 = ORIENTED_EDGE('',*,*,#261,.T.);
|
||||
#261 = EDGE_CURVE('',#262,#262,#264,.T.);
|
||||
#262 = VERTEX_POINT('',#263);
|
||||
#263 = CARTESIAN_POINT('',(0.,7.959899496853,-1.E-15));
|
||||
#264 = B_SPLINE_CURVE_WITH_KNOTS('',6,(#265,#266,#267,#268,#269,#270,
|
||||
#271,#272,#273,#274,#275,#276,#277,#278,#279,#280,#281,#282,#283,
|
||||
#284,#285,#286,#287,#288,#289,#290,#291,#292,#293,#294,#295,#296,
|
||||
#297,#298,#299,#300,#301,#302,#303,#304,#305,#306,#307,#308,#309,
|
||||
#310,#311,#312,#313,#314,#315,#316,#317,#318,#319,#320,#321,#322,
|
||||
#323,#324,#325,#326,#327,#328,#329,#330,#331,#332,#333,#334,#335,
|
||||
#336,#337,#338,#339,#340,#341,#342,#343,#344,#345,#346,#347,#348,
|
||||
#349,#350,#351,#352,#353,#354,#355,#356,#357,#358,#359,#360,#361,
|
||||
#362,#363,#364,#365,#366,#367,#368,#369,#370,#371,#372,#373,#374,
|
||||
#375,#376,#377,#378,#379,#380,#381,#382,#383,#384,#385,#386,#387,
|
||||
#388,#389,#390,#391,#392,#393,#394,#395,#396,#397,#398,#399,#400,
|
||||
#401,#402,#403,#404,#405,#406,#407,#408,#409,#410,#411,#412,#413,
|
||||
#414,#415,#416,#417,#418,#419,#420,#421,#422,#423,#424,#425,#426,
|
||||
#427,#428,#429,#430,#431),.UNSPECIFIED.,.T.,.F.,(7,5,5,5,5,5,5,5,5,5
|
||||
,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,7),(0.,
|
||||
1.900049665728E-02,4.200109787471E-02,6.400167295201E-02,
|
||||
9.300243101292E-02,0.108002823111,0.132003450468,0.161004208529,
|
||||
0.185004835887,0.214005593948,0.237006195165,0.266006953226,
|
||||
0.295007711287,0.319008338645,0.347009070565,0.374009776344,
|
||||
0.403010534405,0.420010978782,0.442011553859,0.462012076656,
|
||||
0.486012704014,0.516013488216,0.545014246277,0.573014978197,
|
||||
0.602015736258,0.627016389756,0.648016938693,0.66801746149,
|
||||
0.691018062708,0.803003200132,0.8520044812,0.876005108557,
|
||||
0.904005840477,1.),.UNSPECIFIED.);
|
||||
#265 = CARTESIAN_POINT('',(1.75E-15,7.959899496853,0.));
|
||||
#266 = CARTESIAN_POINT('',(0.158376135629,7.959899497669,0.));
|
||||
#267 = CARTESIAN_POINT('',(0.316752351616,7.956118089688,0.));
|
||||
#268 = CARTESIAN_POINT('',(0.475015617318,7.948555279547,0.));
|
||||
#269 = CARTESIAN_POINT('',(0.633053169985,7.937215556685,0.));
|
||||
#270 = CARTESIAN_POINT('',(0.790752514768,7.922107893339,0.));
|
||||
#271 = CARTESIAN_POINT('',(1.138355338001,7.880412624282,0.));
|
||||
#272 = CARTESIAN_POINT('',(1.328049519396,7.852077734661,0.));
|
||||
#273 = CARTESIAN_POINT('',(1.516884753828,7.81826499274,0.));
|
||||
#274 = CARTESIAN_POINT('',(1.704663662893,7.779007782072,0.));
|
||||
#275 = CARTESIAN_POINT('',(1.891190704861,7.734348952714,0.));
|
||||
#276 = CARTESIAN_POINT('',(2.253306633934,7.63650695822,0.));
|
||||
#277 = CARTESIAN_POINT('',(2.429018836525,7.583778745675,0.));
|
||||
#278 = CARTESIAN_POINT('',(2.603239302083,7.526201992934,0.));
|
||||
#279 = CARTESIAN_POINT('',(2.775801188964,7.463830138133,0.));
|
||||
#280 = CARTESIAN_POINT('',(2.946540294248,7.396724248196,0.));
|
||||
#281 = CARTESIAN_POINT('',(3.337744400845,7.23034554941,0.));
|
||||
#282 = CARTESIAN_POINT('',(3.556746610884,7.127631172373,0.));
|
||||
#283 = CARTESIAN_POINT('',(3.771931109825,7.016967583615,0.));
|
||||
#284 = CARTESIAN_POINT('',(3.982938862859,6.898533945567,0.));
|
||||
#285 = CARTESIAN_POINT('',(4.189422374394,6.772530887207,0.));
|
||||
#286 = CARTESIAN_POINT('',(4.495333685936,6.570206123623,0.));
|
||||
#287 = CARTESIAN_POINT('',(4.598321567445,6.4992659464,0.));
|
||||
#288 = CARTESIAN_POINT('',(4.699962971259,6.426390636936,0.));
|
||||
#289 = CARTESIAN_POINT('',(4.800212565896,6.351612267911,0.));
|
||||
#290 = CARTESIAN_POINT('',(4.899026049716,6.274964320139,0.));
|
||||
#291 = CARTESIAN_POINT('',(5.152094667792,6.07090950342,0.));
|
||||
#292 = CARTESIAN_POINT('',(5.304042208553,5.940640308207,0.));
|
||||
#293 = CARTESIAN_POINT('',(5.45202528765,5.805817242063,0.));
|
||||
#294 = CARTESIAN_POINT('',(5.595874245688,5.666591784137,0.));
|
||||
#295 = CARTESIAN_POINT('',(5.735427249427,5.523123747585,0.));
|
||||
#296 = CARTESIAN_POINT('',(6.033779745648,5.197300879326,0.));
|
||||
#297 = CARTESIAN_POINT('',(6.190532634937,5.013070743487,0.));
|
||||
#298 = CARTESIAN_POINT('',(6.340517043336,4.823187948851,0.));
|
||||
#299 = CARTESIAN_POINT('',(6.483480451658,4.627964327476,0.));
|
||||
#300 = CARTESIAN_POINT('',(6.619189737838,4.42772646668,0.));
|
||||
#301 = CARTESIAN_POINT('',(6.853562076147,4.05323432989,0.));
|
||||
#302 = CARTESIAN_POINT('',(6.954578645737,3.880451891451,0.));
|
||||
#303 = CARTESIAN_POINT('',(7.050359943099,3.704661692808,0.));
|
||||
#304 = CARTESIAN_POINT('',(7.140795146125,3.526062350936,0.));
|
||||
#305 = CARTESIAN_POINT('',(7.225783553202,3.344857800697,0.));
|
||||
#306 = CARTESIAN_POINT('',(7.401237885347,2.939406779224,0.));
|
||||
#307 = CARTESIAN_POINT('',(7.489156626833,2.714057037385,0.));
|
||||
#308 = CARTESIAN_POINT('',(7.56883094046,2.485577711821,0.));
|
||||
#309 = CARTESIAN_POINT('',(7.640124113834,2.254346048998,0.));
|
||||
#310 = CARTESIAN_POINT('',(7.702922589384,2.020746899341,0.));
|
||||
#311 = CARTESIAN_POINT('',(7.800132809305,1.598337937264,0.));
|
||||
#312 = CARTESIAN_POINT('',(7.837729669059,1.410260228425,0.));
|
||||
#313 = CARTESIAN_POINT('',(7.86988156502,1.221135129971,0.));
|
||||
#314 = CARTESIAN_POINT('',(7.896553047583,1.031159659721,0.));
|
||||
#315 = CARTESIAN_POINT('',(7.917718196142,0.840532314065,0.));
|
||||
#316 = CARTESIAN_POINT('',(7.953083678491,0.408527174328,0.));
|
||||
#317 = CARTESIAN_POINT('',(7.964026809915,0.166881857539,0.));
|
||||
#318 = CARTESIAN_POINT('',(7.96615723779,-7.508148625279E-02,0.));
|
||||
#319 = CARTESIAN_POINT('',(7.959466556881,-0.316961690591,0.));
|
||||
#320 = CARTESIAN_POINT('',(7.943970732293,-0.558357818713,0.));
|
||||
#321 = CARTESIAN_POINT('',(7.895449487125,-1.039380510448,0.));
|
||||
#322 = CARTESIAN_POINT('',(7.862423933096,-1.27900839704,0.));
|
||||
#323 = CARTESIAN_POINT('',(7.820673939692,-1.517352133087,0.));
|
||||
#324 = CARTESIAN_POINT('',(7.770263934502,-1.754015670283,0.));
|
||||
#325 = CARTESIAN_POINT('',(7.71128227038,-1.988607602261,0.));
|
||||
#326 = CARTESIAN_POINT('',(7.588027935622,-2.412851780118,0.));
|
||||
#327 = CARTESIAN_POINT('',(7.526420639738,-2.603279350229,0.));
|
||||
#328 = CARTESIAN_POINT('',(7.459082978329,-2.791804919369,0.));
|
||||
#329 = CARTESIAN_POINT('',(7.38608929913,-2.978213539503,0.));
|
||||
#330 = CARTESIAN_POINT('',(7.307524657077,-3.162294270118,0.));
|
||||
#331 = CARTESIAN_POINT('',(7.125438372101,-3.555643678098,0.));
|
||||
#332 = CARTESIAN_POINT('',(7.019939177843,-3.76399782198,0.));
|
||||
#333 = CARTESIAN_POINT('',(6.907139533304,-3.96857376379,0.));
|
||||
#334 = CARTESIAN_POINT('',(6.787210104706,-4.16905320895,0.));
|
||||
#335 = CARTESIAN_POINT('',(6.66033992272,-4.365128414381,0.));
|
||||
#336 = CARTESIAN_POINT('',(6.39790439774,-4.741041209999,0.));
|
||||
#337 = CARTESIAN_POINT('',(6.262810860483,-4.921209429162,0.));
|
||||
#338 = CARTESIAN_POINT('',(6.12164178035,-5.096740501076,0.));
|
||||
#339 = CARTESIAN_POINT('',(5.974597240315,-5.267379797952,0.));
|
||||
#340 = CARTESIAN_POINT('',(5.821891396666,-5.432884409128,0.));
|
||||
#341 = CARTESIAN_POINT('',(5.493899612152,-5.765023980942,0.));
|
||||
#342 = CARTESIAN_POINT('',(5.317778109013,-5.930835402093,0.));
|
||||
#343 = CARTESIAN_POINT('',(5.135671008924,-6.090170905226,0.));
|
||||
#344 = CARTESIAN_POINT('',(4.94787706243,-6.242762622309,0.));
|
||||
#345 = CARTESIAN_POINT('',(4.754710731296,-6.388361316561,0.));
|
||||
#346 = CARTESIAN_POINT('',(4.440310899176,-6.607852860349,0.));
|
||||
#347 = CARTESIAN_POINT('',(4.32238669345,-6.686487188298,0.));
|
||||
#348 = CARTESIAN_POINT('',(4.202795938003,-6.762593037977,0.));
|
||||
#349 = CARTESIAN_POINT('',(4.08160651871,-6.836126525555,0.));
|
||||
#350 = CARTESIAN_POINT('',(3.958887840648,-6.907046211699,0.));
|
||||
#351 = CARTESIAN_POINT('',(3.67401119217,-7.063658476263,0.));
|
||||
#352 = CARTESIAN_POINT('',(3.51086900792,-7.1475612191,0.));
|
||||
#353 = CARTESIAN_POINT('',(3.345438129381,-7.226936766351,0.));
|
||||
#354 = CARTESIAN_POINT('',(3.177875801637,-7.301707879731,0.));
|
||||
#355 = CARTESIAN_POINT('',(3.008342660805,-7.371804646398,0.));
|
||||
#356 = CARTESIAN_POINT('',(2.681239152791,-7.496582516025,0.));
|
||||
#357 = CARTESIAN_POINT('',(2.523982134607,-7.552085819632,0.));
|
||||
#358 = CARTESIAN_POINT('',(2.365354878931,-7.603627403417,0.));
|
||||
#359 = CARTESIAN_POINT('',(2.205482222939,-7.651165545667,0.));
|
||||
#360 = CARTESIAN_POINT('',(2.044490641548,-7.694663789319,0.));
|
||||
#361 = CARTESIAN_POINT('',(1.688129411634,-7.781403520843,0.));
|
||||
#362 = CARTESIAN_POINT('',(1.492323407707,-7.822853818679,0.));
|
||||
#363 = CARTESIAN_POINT('',(1.295311790344,-7.858387935907,0.));
|
||||
#364 = CARTESIAN_POINT('',(1.09731797362,-7.887963253361,0.));
|
||||
#365 = CARTESIAN_POINT('',(0.898567230977,-7.911548432262,0.));
|
||||
#366 = CARTESIAN_POINT('',(0.450186155614,-7.951092147012,0.));
|
||||
#367 = CARTESIAN_POINT('',(0.200256625253,-7.963670023027,0.));
|
||||
#368 = CARTESIAN_POINT('',(-5.005765232589E-02,-7.966817966631,0.));
|
||||
#369 = CARTESIAN_POINT('',(-0.300312608957,-7.96052481238,0.));
|
||||
#370 = CARTESIAN_POINT('',(-0.550064351841,-7.944807305014,0.));
|
||||
#371 = CARTESIAN_POINT('',(-1.039380510447,-7.895449487125,0.));
|
||||
#372 = CARTESIAN_POINT('',(-1.27900839704,-7.862423933096,0.));
|
||||
#373 = CARTESIAN_POINT('',(-1.517352133087,-7.820673939692,0.));
|
||||
#374 = CARTESIAN_POINT('',(-1.754015670283,-7.770263934502,0.));
|
||||
#375 = CARTESIAN_POINT('',(-1.988607602261,-7.71128227038,0.));
|
||||
#376 = CARTESIAN_POINT('',(-2.444870148825,-7.578725744846,0.));
|
||||
#377 = CARTESIAN_POINT('',(-2.666708515307,-7.505723906384,0.));
|
||||
#378 = CARTESIAN_POINT('',(-2.885908270162,-7.424936882384,0.));
|
||||
#379 = CARTESIAN_POINT('',(-3.102129092468,-7.336485586311,0.));
|
||||
#380 = CARTESIAN_POINT('',(-3.315038334248,-7.240510672782,0.));
|
||||
#381 = CARTESIAN_POINT('',(-3.741057717922,-7.030143782343,0.));
|
||||
#382 = CARTESIAN_POINT('',(-3.953904597841,-6.915215905132,0.));
|
||||
#383 = CARTESIAN_POINT('',(-4.162490591287,-6.792567294051,0.));
|
||||
#384 = CARTESIAN_POINT('',(-4.366467363345,-6.662397117274,0.));
|
||||
#385 = CARTESIAN_POINT('',(-4.565499313135,-6.524925323022,0.));
|
||||
#386 = CARTESIAN_POINT('',(-4.926301794497,-6.255795466567,0.));
|
||||
#387 = CARTESIAN_POINT('',(-5.08942592189,-6.125950428497,0.));
|
||||
#388 = CARTESIAN_POINT('',(-5.248429351289,-5.991011678914,0.));
|
||||
#389 = CARTESIAN_POINT('',(-5.403114347759,-5.851143496091,0.));
|
||||
#390 = CARTESIAN_POINT('',(-5.553292046128,-5.706520283009,0.));
|
||||
#391 = CARTESIAN_POINT('',(-5.820994420566,-5.432003823187,0.));
|
||||
#392 = CARTESIAN_POINT('',(-5.939899288526,-5.303455881577,0.));
|
||||
#393 = CARTESIAN_POINT('',(-6.055390477644,-5.171792049896,0.));
|
||||
#394 = CARTESIAN_POINT('',(-6.167366506686,-5.037125987889,0.));
|
||||
#395 = CARTESIAN_POINT('',(-6.275730990412,-4.899575707687,0.));
|
||||
#396 = CARTESIAN_POINT('',(-6.480070406405,-4.625632966639,0.));
|
||||
#397 = CARTESIAN_POINT('',(-6.576389727827,-4.489497097743,0.));
|
||||
#398 = CARTESIAN_POINT('',(-6.669271774128,-4.350961663368,0.));
|
||||
#399 = CARTESIAN_POINT('',(-6.758642333318,-4.210135372312,0.));
|
||||
#400 = CARTESIAN_POINT('',(-6.844431811118,-4.067129945918,0.));
|
||||
#401 = CARTESIAN_POINT('',(-7.021040154892,-3.755229839557,0.));
|
||||
#402 = CARTESIAN_POINT('',(-7.110683334025,-3.585669086845,0.));
|
||||
#403 = CARTESIAN_POINT('',(-7.195405922937,-3.413552468801,0.));
|
||||
#404 = CARTESIAN_POINT('',(-7.275117788537,-3.239058727476,0.));
|
||||
#405 = CARTESIAN_POINT('',(-7.349737510064,-3.062370738114,0.));
|
||||
#406 = CARTESIAN_POINT('',(-7.757352875972,-2.013649693212,0.));
|
||||
#407 = CARTESIAN_POINT('',(-7.973282061516,-1.095508224133,0.));
|
||||
#408 = CARTESIAN_POINT('',(-8.057895117922,-0.151905736054,0.));
|
||||
#409 = CARTESIAN_POINT('',(-8.008906731552,0.794215378184,0.));
|
||||
#410 = CARTESIAN_POINT('',(-7.827735889287,1.719842893081,0.));
|
||||
#411 = CARTESIAN_POINT('',(-7.389092770737,2.987998676376,0.));
|
||||
#412 = CARTESIAN_POINT('',(-7.231806238386,3.3657885693,0.));
|
||||
#413 = CARTESIAN_POINT('',(-7.05138858479,3.733518071333,0.));
|
||||
#414 = CARTESIAN_POINT('',(-6.848656421229,4.089432648516,0.));
|
||||
#415 = CARTESIAN_POINT('',(-6.624601885028,4.431870703153,0.));
|
||||
#416 = CARTESIAN_POINT('',(-6.260779345283,4.919620275415,0.));
|
||||
#417 = CARTESIAN_POINT('',(-6.136329640465,5.076369723055,0.));
|
||||
#418 = CARTESIAN_POINT('',(-6.007179879082,5.229329162117,0.));
|
||||
#419 = CARTESIAN_POINT('',(-5.873475038158,5.378323344476,0.));
|
||||
#420 = CARTESIAN_POINT('',(-5.735368717774,5.523184528493,0.));
|
||||
#421 = CARTESIAN_POINT('',(-5.426953360878,5.827748379869,0.));
|
||||
#422 = CARTESIAN_POINT('',(-5.255112824258,5.985901341385,0.));
|
||||
#423 = CARTESIAN_POINT('',(-5.07775944125,6.137956769802,0.));
|
||||
#424 = CARTESIAN_POINT('',(-4.895164622458,6.283676390738,0.));
|
||||
#425 = CARTESIAN_POINT('',(-4.707613279045,6.422838249183,0.));
|
||||
#426 = CARTESIAN_POINT('',(-3.856458223064,7.009134450453,0.));
|
||||
#427 = CARTESIAN_POINT('',(-3.142544053826,7.383692786948,0.));
|
||||
#428 = CARTESIAN_POINT('',(-2.386074209915,7.670231393488,0.));
|
||||
#429 = CARTESIAN_POINT('',(-1.600552180122,7.863379363923,0.));
|
||||
#430 = CARTESIAN_POINT('',(-0.800145275911,7.959899704658,0.));
|
||||
#431 = CARTESIAN_POINT('',(-2.E-17,7.959899496853,0.));
|
||||
#432 = PLANE('',#433);
|
||||
#433 = AXIS2_PLACEMENT_3D('',#434,#435,#436);
|
||||
#434 = CARTESIAN_POINT('',(-9.1E-16,-3.E-17,0.));
|
||||
#435 = DIRECTION('',(0.,0.,1.));
|
||||
#436 = DIRECTION('',(1.,0.,0.));
|
||||
#437 = ADVANCED_FACE('',(#438,#441),#452,.T.);
|
||||
#438 = FACE_BOUND('',#439,.T.);
|
||||
#439 = EDGE_LOOP('',(#440));
|
||||
#440 = ORIENTED_EDGE('',*,*,#241,.T.);
|
||||
#441 = FACE_BOUND('',#442,.T.);
|
||||
#442 = EDGE_LOOP('',(#443));
|
||||
#443 = ORIENTED_EDGE('',*,*,#444,.F.);
|
||||
#444 = EDGE_CURVE('',#445,#445,#447,.T.);
|
||||
#445 = VERTEX_POINT('',#446);
|
||||
#446 = CARTESIAN_POINT('',(3.95,0.,10.6));
|
||||
#447 = CIRCLE('',#448,3.95);
|
||||
#448 = AXIS2_PLACEMENT_3D('',#449,#450,#451);
|
||||
#449 = CARTESIAN_POINT('',(0.,0.,10.6));
|
||||
#450 = DIRECTION('',(0.,0.,1.));
|
||||
#451 = DIRECTION('',(1.,0.,0.));
|
||||
#452 = PLANE('',#453);
|
||||
#453 = AXIS2_PLACEMENT_3D('',#454,#455,#456);
|
||||
#454 = CARTESIAN_POINT('',(-3.6E-16,6.E-17,10.6));
|
||||
#455 = DIRECTION('',(0.,0.,1.));
|
||||
#456 = DIRECTION('',(1.,0.,0.));
|
||||
#457 = ADVANCED_FACE('',(#458),#479,.F.);
|
||||
#458 = FACE_BOUND('',#459,.F.);
|
||||
#459 = EDGE_LOOP('',(#460,#461,#468,#477,#478));
|
||||
#460 = ORIENTED_EDGE('',*,*,#261,.F.);
|
||||
#461 = ORIENTED_EDGE('',*,*,#462,.T.);
|
||||
#462 = EDGE_CURVE('',#262,#198,#463,.T.);
|
||||
#463 = ELLIPSE('',#464,8.,4.);
|
||||
#464 = AXIS2_PLACEMENT_3D('',#465,#466,#467);
|
||||
#465 = CARTESIAN_POINT('',(0.,0.,0.4));
|
||||
#466 = DIRECTION('',(1.,0.,-0.));
|
||||
#467 = DIRECTION('',(0.,1.,4.7E-16));
|
||||
#468 = ORIENTED_EDGE('',*,*,#469,.T.);
|
||||
#469 = EDGE_CURVE('',#198,#470,#472,.T.);
|
||||
#470 = VERTEX_POINT('',#471);
|
||||
#471 = CARTESIAN_POINT('',(0.,-1.13E-13,4.4));
|
||||
#472 = ELLIPSE('',#473,8.,4.);
|
||||
#473 = AXIS2_PLACEMENT_3D('',#474,#475,#476);
|
||||
#474 = CARTESIAN_POINT('',(0.,0.,0.4));
|
||||
#475 = DIRECTION('',(1.,0.,-0.));
|
||||
#476 = DIRECTION('',(0.,1.,4.7E-16));
|
||||
#477 = ORIENTED_EDGE('',*,*,#469,.F.);
|
||||
#478 = ORIENTED_EDGE('',*,*,#462,.F.);
|
||||
#479 = SURFACE_OF_REVOLUTION('',#480,#485);
|
||||
#480 = ELLIPSE('',#481,8.,4.);
|
||||
#481 = AXIS2_PLACEMENT_3D('',#482,#483,#484);
|
||||
#482 = CARTESIAN_POINT('',(0.,0.,0.4));
|
||||
#483 = DIRECTION('',(1.,0.,-0.));
|
||||
#484 = DIRECTION('',(0.,1.,4.7E-16));
|
||||
#485 = AXIS1_PLACEMENT('',#486,#487);
|
||||
#486 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#487 = DIRECTION('',(0.,0.,1.));
|
||||
#488 = ADVANCED_FACE('',(#489),#508,.T.);
|
||||
#489 = FACE_BOUND('',#490,.F.);
|
||||
#490 = EDGE_LOOP('',(#491,#499,#506,#507));
|
||||
#491 = ORIENTED_EDGE('',*,*,#492,.T.);
|
||||
#492 = EDGE_CURVE('',#445,#493,#495,.T.);
|
||||
#493 = VERTEX_POINT('',#494);
|
||||
#494 = CARTESIAN_POINT('',(3.95,0.,17.6));
|
||||
#495 = LINE('',#496,#497);
|
||||
#496 = CARTESIAN_POINT('',(3.95,0.,10.6));
|
||||
#497 = VECTOR('',#498,1.);
|
||||
#498 = DIRECTION('',(0.,0.,1.));
|
||||
#499 = ORIENTED_EDGE('',*,*,#500,.T.);
|
||||
#500 = EDGE_CURVE('',#493,#493,#501,.T.);
|
||||
#501 = CIRCLE('',#502,3.95);
|
||||
#502 = AXIS2_PLACEMENT_3D('',#503,#504,#505);
|
||||
#503 = CARTESIAN_POINT('',(0.,0.,17.6));
|
||||
#504 = DIRECTION('',(0.,0.,1.));
|
||||
#505 = DIRECTION('',(1.,0.,0.));
|
||||
#506 = ORIENTED_EDGE('',*,*,#492,.F.);
|
||||
#507 = ORIENTED_EDGE('',*,*,#444,.F.);
|
||||
#508 = CYLINDRICAL_SURFACE('',#509,3.95);
|
||||
#509 = AXIS2_PLACEMENT_3D('',#510,#511,#512);
|
||||
#510 = CARTESIAN_POINT('',(0.,0.,10.6));
|
||||
#511 = DIRECTION('',(-0.,-0.,-1.));
|
||||
#512 = DIRECTION('',(1.,0.,0.));
|
||||
#513 = ADVANCED_FACE('',(#514),#534,.T.);
|
||||
#514 = FACE_BOUND('',#515,.T.);
|
||||
#515 = EDGE_LOOP('',(#516,#525,#526,#527));
|
||||
#516 = ORIENTED_EDGE('',*,*,#517,.F.);
|
||||
#517 = EDGE_CURVE('',#493,#518,#520,.T.);
|
||||
#518 = VERTEX_POINT('',#519);
|
||||
#519 = CARTESIAN_POINT('',(2.95,0.,18.6));
|
||||
#520 = CIRCLE('',#521,1.);
|
||||
#521 = AXIS2_PLACEMENT_3D('',#522,#523,#524);
|
||||
#522 = CARTESIAN_POINT('',(2.95,0.,17.6));
|
||||
#523 = DIRECTION('',(0.,-1.,0.));
|
||||
#524 = DIRECTION('',(1.,0.,0.));
|
||||
#525 = ORIENTED_EDGE('',*,*,#500,.T.);
|
||||
#526 = ORIENTED_EDGE('',*,*,#517,.T.);
|
||||
#527 = ORIENTED_EDGE('',*,*,#528,.F.);
|
||||
#528 = EDGE_CURVE('',#518,#518,#529,.T.);
|
||||
#529 = CIRCLE('',#530,2.95);
|
||||
#530 = AXIS2_PLACEMENT_3D('',#531,#532,#533);
|
||||
#531 = CARTESIAN_POINT('',(0.,0.,18.6));
|
||||
#532 = DIRECTION('',(0.,0.,1.));
|
||||
#533 = DIRECTION('',(1.,0.,0.));
|
||||
#534 = TOROIDAL_SURFACE('',#535,2.95,1.);
|
||||
#535 = AXIS2_PLACEMENT_3D('',#536,#537,#538);
|
||||
#536 = CARTESIAN_POINT('',(0.,0.,17.6));
|
||||
#537 = DIRECTION('',(0.,0.,1.));
|
||||
#538 = DIRECTION('',(1.,0.,0.));
|
||||
#539 = ADVANCED_FACE('',(#540),#543,.T.);
|
||||
#540 = FACE_BOUND('',#541,.T.);
|
||||
#541 = EDGE_LOOP('',(#542));
|
||||
#542 = ORIENTED_EDGE('',*,*,#528,.T.);
|
||||
#543 = PLANE('',#544);
|
||||
#544 = AXIS2_PLACEMENT_3D('',#545,#546,#547);
|
||||
#545 = CARTESIAN_POINT('',(-3.5E-16,1.E-17,18.6));
|
||||
#546 = DIRECTION('',(0.,0.,1.));
|
||||
#547 = DIRECTION('',(1.,0.,0.));
|
||||
#548 = ( GEOMETRIC_REPRESENTATION_CONTEXT(3)
|
||||
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#552)) GLOBAL_UNIT_ASSIGNED_CONTEXT
|
||||
((#549,#550,#551)) REPRESENTATION_CONTEXT('Context #1',
|
||||
'3D Context with UNIT and UNCERTAINTY') );
|
||||
#549 = ( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.) );
|
||||
#550 = ( NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.) );
|
||||
#551 = ( NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT() );
|
||||
#552 = UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(2.E-07),#549,
|
||||
'distance_accuracy_value','confusion accuracy');
|
||||
#553 = PRODUCT_RELATED_PRODUCT_CATEGORY('part',$,(#7));
|
||||
#554 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION('',(#555)
|
||||
,#548);
|
||||
#555 = STYLED_ITEM('color',(#556),#15);
|
||||
#556 = PRESENTATION_STYLE_ASSIGNMENT((#557,#563));
|
||||
#557 = SURFACE_STYLE_USAGE(.BOTH.,#558);
|
||||
#558 = SURFACE_SIDE_STYLE('',(#559));
|
||||
#559 = SURFACE_STYLE_FILL_AREA(#560);
|
||||
#560 = FILL_AREA_STYLE('',(#561));
|
||||
#561 = FILL_AREA_STYLE_COLOUR('',#562);
|
||||
#562 = COLOUR_RGB('',0.650980421574,0.411764706019,0.956862747054);
|
||||
#563 = CURVE_STYLE('',#564,POSITIVE_LENGTH_MEASURE(0.1),#565);
|
||||
#564 = DRAUGHTING_PRE_DEFINED_CURVE_FONT('continuous');
|
||||
#565 = COLOUR_RGB('',9.803921802644E-02,9.803921802644E-02,
|
||||
9.803921802644E-02);
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
62312
CAD/Zusatzteile-EiAndockerRechtsTPU.obj
Normal file
62312
CAD/Zusatzteile-EiAndockerRechtsTPU.obj
Normal file
File diff suppressed because it is too large
Load Diff
BIN
CAD/Zusatzteile.FCStd
Normal file
BIN
CAD/Zusatzteile.FCStd
Normal file
Binary file not shown.
109
EggDuino.ino
109
EggDuino.ino
@@ -1,109 +0,0 @@
|
||||
/* Eggduino-Firmware by Joachim Cerny, 2014
|
||||
|
||||
Thanks for the nice libs ACCELSTEPPER and SERIALCOMMAND, which made this project much easier.
|
||||
Thanks to the Eggbot-Team for such a funny and enjoable concept!
|
||||
Thanks to my wife and my daughter for their patience. :-)
|
||||
|
||||
*/
|
||||
|
||||
// implemented Eggbot-Protocol-Version v13
|
||||
// EBB-Command-Reference, I sourced from: http://www.schmalzhaus.com/EBB/EBBCommands.html
|
||||
// no homing sequence, switch-on position of pen will be taken as reference point.
|
||||
// No collision-detection!!
|
||||
// Supported Servos: I do not know, I use Arduino Servo Lib with TG9e- standard servo.
|
||||
// Note: Maximum-Speed in Inkscape is 1000 Steps/s. You could enter more, but then Pythonscript sends nonsense.
|
||||
// EBB-Coordinates are coming in for 16th-Microstepmode. The Coordinate-Transforms are done in weired integer-math. Be careful, when you diecide to modify settings.
|
||||
|
||||
/* TODOs:
|
||||
1 collision control via penMin/penMax
|
||||
2 implement homing sequence via microswitch or optical device
|
||||
*/
|
||||
|
||||
#include "AccelStepper.h" // nice lib from http://www.airspayce.com/mikem/arduino/AccelStepper/
|
||||
#include <Servo.h>
|
||||
#include "SerialCommand.h" //nice lib from Stefan Rado, https://github.com/kroimon/Arduino-SerialCommand
|
||||
#include <avr/eeprom.h>
|
||||
#include "button.h"
|
||||
|
||||
#define initSting "EBBv13_and_above Protocol emulated by Eggduino-Firmware V1.6a"
|
||||
//Rotational Stepper:
|
||||
#define step1 11
|
||||
#define dir1 10
|
||||
#define enableRotMotor 9
|
||||
#define rotMicrostep 16 //MicrostepMode, only 1,2,4,8,16 allowed, because of Integer-Math in this Sketch
|
||||
//Pen Stepper:
|
||||
#define step2 8
|
||||
#define dir2 7
|
||||
#define enablePenMotor 6
|
||||
#define penMicrostep 16 //MicrostepMode, only 1,2,4,8,16 allowed, because of Integer-Math in this Sketch
|
||||
|
||||
#define servoPin 3 //Servo
|
||||
|
||||
// EXTRAFEATURES - UNCOMMENT TO USE THEM -------------------------------------------------------------------
|
||||
|
||||
// #define prgButton 2 // PRG button
|
||||
// #define penToggleButton 12 // pen up/down button
|
||||
// #define motorsButton 4 // motors enable button
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
|
||||
#define penUpPosEEAddress ((uint16_t *)0)
|
||||
#define penDownPosEEAddress ((uint16_t *)2)
|
||||
|
||||
//make Objects
|
||||
AccelStepper rotMotor(1, step1, dir1);
|
||||
AccelStepper penMotor(1, step2, dir2);
|
||||
Servo penServo;
|
||||
SerialCommand SCmd;
|
||||
//create Buttons
|
||||
#ifdef prgButton
|
||||
Button prgButtonToggle(prgButton, setprgButtonState);
|
||||
#endif
|
||||
#ifdef penToggleButton
|
||||
Button penToggle(penToggleButton, doTogglePen);
|
||||
#endif
|
||||
#ifdef motorsButton
|
||||
Button motorsToggle(motorsButton, toggleMotors);
|
||||
#endif
|
||||
// Variables... be careful, by messing around here, everything has a reason and crossrelations...
|
||||
int penMin=0;
|
||||
int penMax=0;
|
||||
int penUpPos=5; //can be overwritten from EBB-Command SC
|
||||
int penDownPos=20; //can be overwritten from EBB-Command SC
|
||||
int servoRateUp=0; //from EBB-Protocol not implemented on machine-side
|
||||
int servoRateDown=0; //from EBB-Protocol not implemented on machine-side
|
||||
long rotStepError=0;
|
||||
long penStepError=0;
|
||||
int penState=penUpPos;
|
||||
uint32_t nodeCount=0;
|
||||
unsigned int layer=0;
|
||||
boolean prgButtonState=0;
|
||||
uint8_t rotStepCorrection = 16/rotMicrostep ; //devide EBB-Coordinates by this factor to get EGGduino-Steps
|
||||
uint8_t penStepCorrection = 16/penMicrostep ; //devide EBB-Coordinates by this factor to get EGGduino-Steps
|
||||
float rotSpeed=0;
|
||||
float penSpeed=0; // these are local variables for Function SteppermotorMove-Command, but for performance-reasons it will be initialized here
|
||||
boolean motorsEnabled = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
makeComInterface();
|
||||
initHardware();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
moveOneStep();
|
||||
|
||||
SCmd.readSerial();
|
||||
|
||||
#ifdef penToggleButton
|
||||
penToggle.check();
|
||||
#endif
|
||||
|
||||
#ifdef motorsButton
|
||||
motorsToggle.check();
|
||||
#endif
|
||||
|
||||
#ifdef prgButton
|
||||
prgButtonToggle.check();
|
||||
#endif
|
||||
}
|
||||
264
Functions.ino
264
Functions.ino
@@ -1,264 +0,0 @@
|
||||
|
||||
void makeComInterface(){
|
||||
SCmd.addCommand("v",sendVersion);
|
||||
SCmd.addCommand("EM",enableMotors);
|
||||
SCmd.addCommand("SC",stepperModeConfigure);
|
||||
SCmd.addCommand("SP",setPen);
|
||||
SCmd.addCommand("SM",stepperMove);
|
||||
SCmd.addCommand("SE",ignore);
|
||||
SCmd.addCommand("TP",togglePen);
|
||||
SCmd.addCommand("PO",ignore); //Engraver command, not implemented, gives fake answer
|
||||
SCmd.addCommand("NI",nodeCountIncrement);
|
||||
SCmd.addCommand("ND",nodeCountDecrement);
|
||||
SCmd.addCommand("SN",setNodeCount);
|
||||
SCmd.addCommand("QN",queryNodeCount);
|
||||
SCmd.addCommand("SL",setLayer);
|
||||
SCmd.addCommand("QL",queryLayer);
|
||||
SCmd.addCommand("QP",queryPen);
|
||||
SCmd.addCommand("QB",queryButton); //"PRG" Button,
|
||||
SCmd.setDefaultHandler(unrecognized); // Handler for command that isn't matched (says "What?")
|
||||
}
|
||||
|
||||
void queryPen() {
|
||||
char state;
|
||||
if (penState==penUpPos)
|
||||
state='1';
|
||||
else
|
||||
state='0';
|
||||
Serial.print(String(state)+"\r\n");
|
||||
sendAck();
|
||||
}
|
||||
|
||||
void queryButton() {
|
||||
Serial.print(String(prgButtonState) +"\r\n");
|
||||
sendAck();
|
||||
prgButtonState = 0;
|
||||
}
|
||||
|
||||
void queryLayer() {
|
||||
Serial.print(String(layer) +"\r\n");
|
||||
sendAck();
|
||||
}
|
||||
|
||||
void setLayer() {
|
||||
uint32_t value=0;
|
||||
char *arg1;
|
||||
arg1 = SCmd.next();
|
||||
if (arg1 != NULL) {
|
||||
value = atoi(arg1);
|
||||
layer=value;
|
||||
sendAck();
|
||||
}
|
||||
else
|
||||
sendError();
|
||||
}
|
||||
|
||||
void queryNodeCount() {
|
||||
Serial.print(String(nodeCount) +"\r\n");
|
||||
sendAck();
|
||||
|
||||
}
|
||||
|
||||
void setNodeCount() {
|
||||
uint32_t value=0;
|
||||
char *arg1;
|
||||
arg1 = SCmd.next();
|
||||
if (arg1 != NULL) {
|
||||
value = atoi(arg1);
|
||||
nodeCount=value;
|
||||
sendAck();
|
||||
}
|
||||
else
|
||||
sendError();
|
||||
}
|
||||
|
||||
void nodeCountIncrement() {
|
||||
nodeCount=nodeCount++;
|
||||
sendAck();
|
||||
}
|
||||
|
||||
void nodeCountDecrement() {
|
||||
nodeCount=nodeCount--;
|
||||
sendAck();
|
||||
}
|
||||
|
||||
void stepperMove() {
|
||||
uint16_t duration=0; //in ms
|
||||
int penStepsEBB=0; //Pen
|
||||
int rotStepsEBB=0; //Rot
|
||||
|
||||
moveToDestination();
|
||||
|
||||
if (!parseSMArgs(&duration, &penStepsEBB, &rotStepsEBB)) {
|
||||
sendError();
|
||||
return;
|
||||
}
|
||||
|
||||
sendAck();
|
||||
|
||||
if ( (penStepsEBB==0) && (rotStepsEBB==0) ) {
|
||||
delay(duration);
|
||||
return;
|
||||
}
|
||||
|
||||
prepareMove(duration, penStepsEBB, rotStepsEBB);
|
||||
}
|
||||
|
||||
void setPen(){
|
||||
int cmd;
|
||||
int value;
|
||||
char *arg;
|
||||
|
||||
moveToDestination();
|
||||
|
||||
arg = SCmd.next();
|
||||
if (arg != NULL) {
|
||||
cmd = atoi(arg);
|
||||
switch (cmd) {
|
||||
case 0:
|
||||
penServo.write(penUpPos);
|
||||
penState=penUpPos;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
penServo.write(penDownPos);
|
||||
penState=penDownPos;
|
||||
break;
|
||||
|
||||
default:
|
||||
sendError();
|
||||
}
|
||||
}
|
||||
char *val;
|
||||
val = SCmd.next();
|
||||
if (val != NULL) {
|
||||
value = atoi(val);
|
||||
sendAck();
|
||||
delay(value);
|
||||
}
|
||||
if (val==NULL && arg !=NULL) {
|
||||
sendAck();
|
||||
delay(500);
|
||||
}
|
||||
// Serial.println("delay");
|
||||
if (val==NULL && arg ==NULL)
|
||||
sendError();
|
||||
}
|
||||
|
||||
void togglePen(){
|
||||
int value;
|
||||
char *arg;
|
||||
|
||||
moveToDestination();
|
||||
|
||||
arg = SCmd.next();
|
||||
if (arg != NULL)
|
||||
value = atoi(arg);
|
||||
else
|
||||
value = 500;
|
||||
|
||||
doTogglePen();
|
||||
sendAck();
|
||||
delay(value);
|
||||
}
|
||||
|
||||
void doTogglePen() {
|
||||
if (penState==penUpPos) {
|
||||
penServo.write(penDownPos);
|
||||
penState=penDownPos;
|
||||
} else {
|
||||
penServo.write(penUpPos);
|
||||
penState=penUpPos;
|
||||
}
|
||||
}
|
||||
|
||||
void enableMotors(){
|
||||
int cmd;
|
||||
int value;
|
||||
char *arg;
|
||||
char *val;
|
||||
arg = SCmd.next();
|
||||
if (arg != NULL)
|
||||
cmd = atoi(arg);
|
||||
val = SCmd.next();
|
||||
if (val != NULL)
|
||||
value = atoi(val);
|
||||
//values parsed
|
||||
if ((arg != NULL) && (val == NULL)){
|
||||
switch (cmd) {
|
||||
case 0: motorsOff();
|
||||
sendAck();
|
||||
break;
|
||||
case 1: motorsOn();
|
||||
sendAck();
|
||||
break;
|
||||
default:
|
||||
sendError();
|
||||
}
|
||||
}
|
||||
//the following implementaion is a little bit cheated, because i did not know, how to implement different values for first and second argument.
|
||||
if ((arg != NULL) && (val != NULL)){
|
||||
switch (value) {
|
||||
case 0: motorsOff();
|
||||
sendAck();
|
||||
break;
|
||||
case 1: motorsOn();
|
||||
sendAck();
|
||||
break;
|
||||
default:
|
||||
sendError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stepperModeConfigure(){
|
||||
int cmd;
|
||||
int value;
|
||||
char *arg;
|
||||
arg = SCmd.next();
|
||||
if (arg != NULL)
|
||||
cmd = atoi(arg);
|
||||
char *val;
|
||||
val = SCmd.next();
|
||||
if (val != NULL)
|
||||
value = atoi(val);
|
||||
if ((arg != NULL) && (val != NULL)){
|
||||
switch (cmd) {
|
||||
case 4: penDownPos= (int) ((float) (value-6000)/(float) 133.3); // transformation from EBB to PWM-Servo
|
||||
storePenDownPosInEE();
|
||||
sendAck();
|
||||
break;
|
||||
case 5: penUpPos= (int)((float) (value-6000)/(float) 133.3); // transformation from EBB to PWM-Servo
|
||||
storePenUpPosInEE();
|
||||
sendAck();
|
||||
break;
|
||||
case 6: //rotMin=value; ignored
|
||||
sendAck();
|
||||
break;
|
||||
case 7: //rotMax=value; ignored
|
||||
sendAck();
|
||||
break;
|
||||
case 11: servoRateUp=value;
|
||||
sendAck();
|
||||
break;
|
||||
case 12: servoRateDown=value;
|
||||
sendAck();
|
||||
break;
|
||||
default:
|
||||
sendError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sendVersion(){
|
||||
Serial.print(initSting);
|
||||
Serial.print("\r\n");
|
||||
}
|
||||
|
||||
void unrecognized(const char *command){
|
||||
sendError();
|
||||
}
|
||||
|
||||
void ignore(){
|
||||
sendAck();
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
void initHardware(){
|
||||
// enable eeprom wait in avr/eeprom.h functions
|
||||
SPMCSR &= ~SELFPRGEN;
|
||||
|
||||
loadPenPosFromEE();
|
||||
|
||||
pinMode(enableRotMotor, OUTPUT);
|
||||
pinMode(enablePenMotor, OUTPUT);
|
||||
|
||||
rotMotor.setMaxSpeed(2000.0);
|
||||
rotMotor.setAcceleration(10000.0);
|
||||
penMotor.setMaxSpeed(2000.0);
|
||||
penMotor.setAcceleration(10000.0);
|
||||
motorsOff();
|
||||
penServo.attach(servoPin);
|
||||
penServo.write(penState);
|
||||
}
|
||||
|
||||
inline void loadPenPosFromEE() {
|
||||
penUpPos = eeprom_read_word(penUpPosEEAddress);
|
||||
penDownPos = eeprom_read_word(penDownPosEEAddress);
|
||||
penState = penUpPos;
|
||||
}
|
||||
|
||||
inline void storePenUpPosInEE() {
|
||||
eeprom_update_word(penUpPosEEAddress, penUpPos);
|
||||
}
|
||||
|
||||
inline void storePenDownPosInEE() {
|
||||
eeprom_update_word(penDownPosEEAddress, penDownPos);
|
||||
}
|
||||
|
||||
inline void sendAck(){
|
||||
Serial.print("OK\r\n");
|
||||
}
|
||||
|
||||
inline void sendError(){
|
||||
Serial.print("unknown CMD\r\n");
|
||||
}
|
||||
|
||||
void motorsOff() {
|
||||
digitalWrite(enableRotMotor, HIGH);
|
||||
digitalWrite(enablePenMotor, HIGH);
|
||||
motorsEnabled = 0;
|
||||
}
|
||||
|
||||
void motorsOn() {
|
||||
digitalWrite(enableRotMotor, LOW) ;
|
||||
digitalWrite(enablePenMotor, LOW) ;
|
||||
motorsEnabled = 1;
|
||||
}
|
||||
|
||||
void toggleMotors() {
|
||||
if (motorsEnabled) {
|
||||
motorsOff();
|
||||
} else {
|
||||
motorsOn();
|
||||
}
|
||||
}
|
||||
|
||||
bool parseSMArgs(uint16_t *duration, int *penStepsEBB, int *rotStepsEBB) {
|
||||
char *arg1;
|
||||
char *arg2;
|
||||
char *arg3;
|
||||
arg1 = SCmd.next();
|
||||
if (arg1 != NULL) {
|
||||
*duration = atoi(arg1);
|
||||
arg2 = SCmd.next();
|
||||
}
|
||||
if (arg2 != NULL) {
|
||||
*penStepsEBB = atoi(arg2);
|
||||
arg3 = SCmd.next();
|
||||
}
|
||||
if (arg3 != NULL) {
|
||||
*rotStepsEBB = atoi(arg3);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void prepareMove(uint16_t duration, int penStepsEBB, int rotStepsEBB) {
|
||||
if (!motorsEnabled) {
|
||||
motorsOn();
|
||||
}
|
||||
if( (1 == rotStepCorrection) && (1 == penStepCorrection) ){ // if coordinatessystems are identical
|
||||
//set Coordinates and Speed
|
||||
rotMotor.move(rotStepsEBB);
|
||||
rotMotor.setSpeed( abs( (float)rotStepsEBB * (float)1000 / (float)duration ) );
|
||||
penMotor.move(penStepsEBB);
|
||||
penMotor.setSpeed( abs( (float)penStepsEBB * (float)1000 / (float)duration ) );
|
||||
} else {
|
||||
//incoming EBB-Steps will be multiplied by 16, then Integer-maths is done, result will be divided by 16
|
||||
// This make thinks here really complicated, but floating point-math kills performance and memory, believe me... I tried...
|
||||
long rotSteps = ( (long)rotStepsEBB * 16 / rotStepCorrection) + (long)rotStepError; //correct incoming EBB-Steps to our microstep-Setting and multiply by 16 to avoid floatingpoint...
|
||||
long penSteps = ( (long)penStepsEBB * 16 / penStepCorrection) + (long)penStepError;
|
||||
|
||||
int rotStepsToGo = (int) (rotSteps/16); //Calc Steps to go, which are possible on our machine
|
||||
int penStepsToGo = (int) (penSteps/16);
|
||||
|
||||
rotStepError = (long)rotSteps - ((long) rotStepsToGo * (long)16); // calc Position-Error, if there is one
|
||||
penStepError = (long)penSteps - ((long) penStepsToGo * (long)16);
|
||||
|
||||
long temp_rotSpeed = ((long)rotStepsToGo * (long)1000 / (long)duration ); // calc Speed in Integer Math
|
||||
long temp_penSpeed = ((long)penStepsToGo * (long)1000 / (long)duration ) ;
|
||||
|
||||
float rotSpeed= (float) abs(temp_rotSpeed); // type cast
|
||||
float penSpeed= (float) abs(temp_penSpeed);
|
||||
|
||||
//set Coordinates and Speed
|
||||
rotMotor.move(rotStepsToGo); // finally, let us set the target position...
|
||||
rotMotor.setSpeed(rotSpeed); // and the Speed!
|
||||
penMotor.move(penStepsToGo);
|
||||
penMotor.setSpeed( penSpeed );
|
||||
}
|
||||
}
|
||||
|
||||
void moveOneStep() {
|
||||
if ( penMotor.distanceToGo() || rotMotor.distanceToGo() ) {
|
||||
penMotor.runSpeedToPosition(); // Moving.... moving... moving....
|
||||
rotMotor.runSpeedToPosition();
|
||||
}
|
||||
}
|
||||
|
||||
void moveToDestination() {
|
||||
while ( penMotor.distanceToGo() || rotMotor.distanceToGo() ) {
|
||||
penMotor.runSpeedToPosition(); // Moving.... moving... moving....
|
||||
rotMotor.runSpeedToPosition();
|
||||
}
|
||||
}
|
||||
|
||||
void setprgButtonState(){
|
||||
prgButtonState = 1;
|
||||
}
|
||||
90
README.md
90
README.md
@@ -1,46 +1,62 @@
|
||||
Eggduino
|
||||
====
|
||||
# Der Sternenlabor EggBot
|
||||
* Basiert auf EggDuino mit folgenden Anpassungen
|
||||
* portiert auf ESP32
|
||||
* Bessere Stepper Library
|
||||
* Einfache Bahnplanung (weniger Verzerrungen)
|
||||
* Stift Servo mit Rampensteuerung
|
||||
* Webinterface für Logging und Einstellungen (Anschlussbelegung)
|
||||
* BLE Interface (http://Eggbot.app)
|
||||
* Web Socket Interace (aktuell ungetestet)
|
||||
* RS232 Kommunikation mit 115200 (zuvor 9600)
|
||||
* Inkscape Plugin AxiDraw_395 (siehe GIT)
|
||||
* Modifizierte ebb_serial.py zur Erkennung
|
||||
* Modifizierte eggbot.py - entfernt Pausen zwischen Bewegungen
|
||||
* Verwendung ohne Inkscape mittels http://EggBot.app
|
||||
|
||||
Arduino Firmware for Eggbot / Spherebot with Inkscape-Integration
|
||||
Ohne angepasstes Inkscape Plugin kann keine Kommunikation zum EggBot aufgenommen werden.
|
||||
|
||||
Version 1.6a
|
||||
tested with Inkscape Portable 0.91, Eggbot Extension and patched eggbot.py
|
||||
# Materialliste
|
||||
|
||||
Regards: Eggduino-Firmware by Joachim Cerny, 2015
|
||||
Schrauben:
|
||||
* Motor Welle Ei: M3x8
|
||||
* Motorbefestigung: 8x M3x10
|
||||
* Motor Welle Stift: M3x12
|
||||
* Klemme für Eihalterung: 2x M3x16
|
||||
* Spannschraube Stift: M3x25
|
||||
* Armgelenk: M3x30
|
||||
* Mutter: 4x M3 **vierkant!**
|
||||
* Platine Schrauben: 4x M2.3x5
|
||||
* Schrauben für Feder: 2x M2.3x5
|
||||
* Scheiben für Feder: 2x M3x6x0.5
|
||||
|
||||
Thanks for the nice libs ACCELSTEPPER and SERIALCOMMAND, which made this project much easier. Thanks to the Eggbot-Team for such a funny and enjoyable concept! Thanks to my wife and my daughter for their patience. :-)
|
||||
Sonstiges:
|
||||
* Zugfeder: 8x17 (entspannt)
|
||||
* O-Ring: 3x 18x2
|
||||
* Kugellager: 608ZZ (8x22x7)
|
||||
|
||||
Features:
|
||||
Elektronik:
|
||||
* 2x Schrittmotor Nema 17 (200 Schritte/Umd.)
|
||||
* 2x Anschlusskabel
|
||||
* 1x Micro Servo
|
||||
* 1x CNC Shield (z.B. AZDelivery)
|
||||
* 2x Schrittmotor Treiber DRV8825
|
||||
* Konfiguriert auf 32 Mikroschritte
|
||||
* Auf 0,5V kalibriert
|
||||
* 1x ESP32 Arduino UNO Formfaktor
|
||||
* 1x Netzteil 12V min. 1A
|
||||
* 1x Micro USB Kabel
|
||||
|
||||
- Implemented Eggbot-Protocol-Version 2.1.0
|
||||
- Turn-on homing: switch-on position of pen will be taken as reference point.
|
||||
- No collision-detection!!
|
||||
- Supported Servos: At least one type ;-) I use Arduino Servo-Lib with TG9e- standard servo.
|
||||
- Full Arduino-Compatible. I used an Arduino Uno
|
||||
- Button-support (3 buttons)
|
||||
3D-Druckteile: https://www.thingiverse.com/thing:3431363 (hoffentlich bald in unserem GIT)
|
||||
|
||||
Tested and fully functional with Inkscape.
|
||||
# Elektronik
|
||||
Damit der ESP32 mit angeschlossenem CNC Shield korrekt funktioniert. Muss zwischen Enable und GND ein 1k Widerstand eingelötet werden.
|
||||

|
||||
|
||||
Installation:
|
||||
## Anschlussbelegung
|
||||
* Ei-Drehen Stepper: Y
|
||||
* Stift-Bewegen Stepper: X
|
||||
* Servo Ctrl Pin: Z-Step
|
||||
* USB Versorgung an ESP32 UNO (nötig)
|
||||
* 12V Netzteil an CNC Shield (nicht ESP32 UNO Buche)
|
||||
|
||||
- Upload Eggduino.ino with Arduino-IDE or similar tool to your Arudino (i.e. Uno)
|
||||
- Disable Autoreset on Arduinoboard (there are several ways to do this... Which one does not matter...)
|
||||
- Install Inkscape Tools wit Eggbot extension. Detailed instructions: (You yust need to complete Steps 1 and 2)
|
||||
http://wiki.evilmadscientist.com/Installing_software
|
||||
|
||||
- Because of an bug in the Eggbot-extension (Function findEiBotBoards()), the Eggduino cannot be detected by default.
|
||||
Hopefully, the guys will fix this later on. But we can fix it on our own.
|
||||
It is quiete easy:
|
||||
|
||||
- Go to your Inkscape-Installationfolder and navigate to subfolder .\App\Inkscape\share\extensions
|
||||
- open File "eggbot.py" in texteditor and search for line:
|
||||
"Try any devices which seem to have EBB boards attached"
|
||||
- comment that block with "#" like this:
|
||||
# Try any devices which seem to have EBB boards attached
|
||||
# for strComPort in eggbot_scan.findEiBotBoards():
|
||||
# serialPort = self.testSerialPort( strComPort )
|
||||
# if serialPort:
|
||||
# self.svgSerialPort = strComPort
|
||||
# return serialPort
|
||||
- In my version lines 1355-1360
|
||||
|
||||
Falls von der Anschlussbelegung abgewichen wird, muss die geänderte Belegung über das Webinterface angepasst werden.
|
||||
|
||||
46
docs/eggbot-ble-serial-protocol.md
Normal file
46
docs/eggbot-ble-serial-protocol.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# EggBot BLE Serial Protocol
|
||||
|
||||
## Scope
|
||||
This transport exposes the same EggBot command protocol as serial, but over BLE GATT.
|
||||
|
||||
- Protocol commands and responses are unchanged.
|
||||
- Framing is unchanged (`\r` command terminator, `\r\n` response lines).
|
||||
|
||||
## BLE GATT Profile
|
||||
- Device name: `EggDuino`
|
||||
- Service UUID: `6e400001-b5a3-f393-e0a9-e50e24dcca9e`
|
||||
- RX characteristic (host -> EggDuino): `6e400002-b5a3-f393-e0a9-e50e24dcca9e`
|
||||
- Properties: `Write`, `Write Without Response`
|
||||
- TX characteristic (EggDuino -> host): `6e400003-b5a3-f393-e0a9-e50e24dcca9e`
|
||||
- Properties: `Notify`, `Read`
|
||||
|
||||
## Data Model
|
||||
- Host writes plain ASCII command bytes to RX.
|
||||
- Firmware parses bytes with the same EggBot command parser used for USB serial.
|
||||
- Firmware sends responses via TX notifications.
|
||||
- Long responses are segmented into BLE-sized notification chunks; host must reassemble by bytes and parse lines by `\r\n`.
|
||||
|
||||
## Compatibility Rules
|
||||
- A BLE client must send commands exactly as serial hosts do.
|
||||
- Each command must end with `\r`.
|
||||
- Data-returning commands (`QP`, `QB`, `QN`, `QL`) return a value line before final status (`OK\r\n`).
|
||||
- Unknown/invalid command format returns `unknown CMD\r\n`.
|
||||
|
||||
## Example BLE Transactions
|
||||
Version query:
|
||||
- Write RX: `v\r`
|
||||
- Notify TX: `EBBv13_and_above Protocol emulated by Eggduino-Firmware V1.6a\r\n`
|
||||
|
||||
Move command:
|
||||
- Write RX: `SM,100,0,200\r`
|
||||
- Notify TX: `OK\r\n`
|
||||
|
||||
Query node count:
|
||||
- Write RX: `QN\r`
|
||||
- Notify TX: `<number>\r\n`
|
||||
- Notify TX: `OK\r\n`
|
||||
|
||||
## Operational Notes
|
||||
- BLE receive bytes are queued and parsed in the main firmware loop.
|
||||
- If the BLE RX queue overruns, excess bytes are dropped and a log entry is generated.
|
||||
- BLE and USB serial can coexist; each command response is routed to the transport that received that command.
|
||||
53
docs/eggbot-serial-protocol.md
Normal file
53
docs/eggbot-serial-protocol.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# EggBot Serial Protocol (USB/UART)
|
||||
|
||||
## Scope
|
||||
This firmware emulates the EggBot/EBB command protocol over serial transport.
|
||||
|
||||
- Transport: USB CDC/UART (`115200 8N1`)
|
||||
- Command separator: `,`
|
||||
- Command terminator: carriage return (`\r`, ASCII 0x0D)
|
||||
- Response terminator: carriage return + newline (`\r\n`)
|
||||
|
||||
## Handshake and Generic Responses
|
||||
- Version query: `v\r`
|
||||
- Version response: `EBBv13_and_above Protocol emulated by Eggduino-Firmware V1.6a\r\n`
|
||||
- Success response: `OK\r\n`
|
||||
- Error response: `unknown CMD\r\n`
|
||||
|
||||
## Implemented Commands
|
||||
All commands are case-sensitive and comma-delimited.
|
||||
|
||||
- `EM,<mode>[,<ignored>]`
|
||||
- `SC,<id>,<value>`
|
||||
- `SP,<penState>[,<delayMs>]`
|
||||
- `SM,<durationMs>,<penSteps>,<rotSteps>`
|
||||
- `SE` (ack-only placeholder)
|
||||
- `TP[,<delayMs>]`
|
||||
- `PO` (ack-only placeholder)
|
||||
- `NI`
|
||||
- `ND`
|
||||
- `SN,<nodeCount>`
|
||||
- `QN`
|
||||
- `SL,<layer>`
|
||||
- `QL`
|
||||
- `QP`
|
||||
- `QB`
|
||||
|
||||
## Command Behavior Notes
|
||||
- `SM`: firmware blocks until previous move completes, sends `OK`, then runs the requested move.
|
||||
- `SP` and `TP`: support optional delay argument in milliseconds.
|
||||
- `QP`: returns pen state line (`1` for pen up, `0` for pen down), then `OK`.
|
||||
- `QB`: returns button state (`0` or `1`), then `OK`; internal button latch is reset after query.
|
||||
- `QN`, `QL`: return numeric line first, then `OK`.
|
||||
- `SC` supports these IDs:
|
||||
- `4`: set pen-down servo position (EBB value to servo-mapped value)
|
||||
- `5`: set pen-up servo position
|
||||
- `6`, `7`: accepted, ignored, and acknowledged
|
||||
- `11`: set servo rate up
|
||||
- `12`: set servo rate down
|
||||
|
||||
## Timing/Parsing Requirements for Hosts
|
||||
- Always terminate each command with `\r`.
|
||||
- Do not rely on `\n` as a command terminator.
|
||||
- Read until `OK\r\n` (or `unknown CMD\r\n`) to complete command transactions.
|
||||
- For commands that return data (`QP`, `QB`, `QN`, `QL`), read one data line plus the final status line.
|
||||
45
docs/eggbot-wifi-serial-protocol.md
Normal file
45
docs/eggbot-wifi-serial-protocol.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# EggBot WiFi Serial Protocol
|
||||
|
||||
## Scope
|
||||
This transport exposes the same EggBot command protocol as serial, but over WiFi/WebSocket.
|
||||
|
||||
- Protocol commands and responses are unchanged.
|
||||
- Framing is unchanged (`\r` command terminator, `\r\n` response lines).
|
||||
|
||||
## Transport Profile
|
||||
- Protocol endpoint: `ws://<eggs-esp32-ip>:1337/`
|
||||
- WebSocket message type: text or binary frames accepted
|
||||
- Payload: ASCII command bytes
|
||||
|
||||
## Data Model
|
||||
- Host writes command bytes to the WebSocket.
|
||||
- Firmware parses incoming bytes with the same EggBot parser used for USB serial and BLE.
|
||||
- Firmware sends responses as WebSocket text messages.
|
||||
- Hosts must treat inbound data as a stream and parse lines by `\r\n`.
|
||||
|
||||
## Compatibility Rules
|
||||
- A WiFi client must send commands exactly as serial hosts do.
|
||||
- Each command must end with `\r`.
|
||||
- Data-returning commands (`QP`, `QB`, `QN`, `QL`) return a value line before final status (`OK\r\n`).
|
||||
- Unknown/invalid command format returns `unknown CMD\r\n`.
|
||||
|
||||
## Example WiFi Transactions
|
||||
Version query:
|
||||
- Send: `v\r`
|
||||
- Receive: `EBBv13_and_above Protocol emulated by Eggduino-Firmware V1.6a\r\n`
|
||||
|
||||
Move command:
|
||||
- Send: `SM,100,0,200\r`
|
||||
- Receive: `OK\r\n`
|
||||
|
||||
Query layer:
|
||||
- Send: `QL\r`
|
||||
- Receive: `<layer>\r\n`
|
||||
- Receive: `OK\r\n`
|
||||
|
||||
## Operational Notes
|
||||
- The WiFi protocol endpoint is started only when ESP32 station WiFi is connected.
|
||||
- Incoming WiFi bytes are queued and parsed in the main loop.
|
||||
- If the WiFi RX queue overruns, excess bytes are dropped and a log entry is generated.
|
||||
- WiFi transport is single active client: first connected sender is accepted until disconnect.
|
||||
- USB serial, BLE, and WiFi can coexist; responses are routed to the transport that received each command.
|
||||
185
docs/examples/eggbot-ble-client.mjs
Normal file
185
docs/examples/eggbot-ble-client.mjs
Normal file
@@ -0,0 +1,185 @@
|
||||
const SERVICE_UUID = '6e400001-b5a3-f393-e0a9-e50e24dcca9e';
|
||||
const RX_UUID = '6e400002-b5a3-f393-e0a9-e50e24dcca9e';
|
||||
const TX_UUID = '6e400003-b5a3-f393-e0a9-e50e24dcca9e';
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
let device = null;
|
||||
let server = null;
|
||||
let rxCharacteristic = null;
|
||||
let txCharacteristic = null;
|
||||
let lineBuffer = '';
|
||||
let commandQueue = Promise.resolve();
|
||||
|
||||
const lineListeners = new Set();
|
||||
|
||||
function emitLine(line) {
|
||||
for (const listener of lineListeners) {
|
||||
listener(line);
|
||||
}
|
||||
}
|
||||
|
||||
function handleNotification(event) {
|
||||
const dataView = event.target.value;
|
||||
const chunk = decoder.decode(dataView, { stream: true });
|
||||
lineBuffer += chunk;
|
||||
|
||||
while (true) {
|
||||
const delimiterIndex = lineBuffer.indexOf('\r\n');
|
||||
if (delimiterIndex < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
const line = lineBuffer.slice(0, delimiterIndex);
|
||||
lineBuffer = lineBuffer.slice(delimiterIndex + 2);
|
||||
emitLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
function handleDisconnected() {
|
||||
server = null;
|
||||
rxCharacteristic = null;
|
||||
if (txCharacteristic) {
|
||||
txCharacteristic.removeEventListener('characteristicvaluechanged', handleNotification);
|
||||
}
|
||||
txCharacteristic = null;
|
||||
}
|
||||
|
||||
async function writeToRxCharacteristic(payloadBytes) {
|
||||
if (!rxCharacteristic) {
|
||||
throw new Error('EggDuino is not connected');
|
||||
}
|
||||
|
||||
if (typeof rxCharacteristic.writeValueWithoutResponse === 'function') {
|
||||
await rxCharacteristic.writeValueWithoutResponse(payloadBytes);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof rxCharacteristic.writeValueWithResponse === 'function') {
|
||||
await rxCharacteristic.writeValueWithResponse(payloadBytes);
|
||||
return;
|
||||
}
|
||||
|
||||
await rxCharacteristic.writeValue(payloadBytes);
|
||||
}
|
||||
|
||||
function normalizeCommand(command) {
|
||||
return command.endsWith('\r') ? command : `${command}\r`;
|
||||
}
|
||||
|
||||
function enqueueCommand(fn) {
|
||||
const run = commandQueue.then(fn, fn);
|
||||
commandQueue = run.catch(() => {});
|
||||
return run;
|
||||
}
|
||||
|
||||
export async function connectEggDuino() {
|
||||
if (server?.connected && rxCharacteristic && txCharacteristic) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
device = await navigator.bluetooth.requestDevice({
|
||||
filters: [{ services: [SERVICE_UUID] }],
|
||||
optionalServices: [SERVICE_UUID],
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error(`BLE request failed: ${error.message}`);
|
||||
}
|
||||
|
||||
device.addEventListener('gattserverdisconnected', handleDisconnected);
|
||||
|
||||
try {
|
||||
server = await device.gatt.connect();
|
||||
} catch (error) {
|
||||
throw new Error(`BLE gatt connect failed: ${error.message}`);
|
||||
}
|
||||
|
||||
let service;
|
||||
try {
|
||||
service = await server.getPrimaryService(SERVICE_UUID);
|
||||
} catch (error) {
|
||||
throw new Error(`BLE service lookup failed: ${error.message}`);
|
||||
}
|
||||
|
||||
try {
|
||||
rxCharacteristic = await service.getCharacteristic(RX_UUID);
|
||||
txCharacteristic = await service.getCharacteristic(TX_UUID);
|
||||
} catch (error) {
|
||||
throw new Error(`BLE characteristic lookup failed: ${error.message}`);
|
||||
}
|
||||
|
||||
try {
|
||||
await txCharacteristic.startNotifications();
|
||||
txCharacteristic.addEventListener('characteristicvaluechanged', handleNotification);
|
||||
} catch (error) {
|
||||
throw new Error(`BLE notifications setup failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function disconnectEggDuino() {
|
||||
if (!device) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (txCharacteristic) {
|
||||
txCharacteristic.removeEventListener('characteristicvaluechanged', handleNotification);
|
||||
}
|
||||
|
||||
if (device.gatt?.connected) {
|
||||
device.gatt.disconnect();
|
||||
}
|
||||
|
||||
handleDisconnected();
|
||||
}
|
||||
|
||||
export async function sendEggBotCommand(command) {
|
||||
const normalized = normalizeCommand(command);
|
||||
await writeToRxCharacteristic(encoder.encode(normalized));
|
||||
}
|
||||
|
||||
export async function sendEggBotCommandExpectOk(command, timeoutMs = 2000) {
|
||||
return enqueueCommand(() => new Promise(async (resolve, reject) => {
|
||||
const collectedLines = [];
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
lineListeners.delete(onResponseLine);
|
||||
reject(new Error('EggBot response timeout'));
|
||||
}, timeoutMs);
|
||||
|
||||
const onResponseLine = (line) => {
|
||||
collectedLines.push(line);
|
||||
|
||||
if (line === 'OK') {
|
||||
clearTimeout(timeout);
|
||||
lineListeners.delete(onResponseLine);
|
||||
resolve(collectedLines.slice(0, -1));
|
||||
}
|
||||
|
||||
if (line === 'unknown CMD') {
|
||||
clearTimeout(timeout);
|
||||
lineListeners.delete(onResponseLine);
|
||||
reject(new Error(`EggBot error: ${collectedLines.join(' | ')}`));
|
||||
}
|
||||
};
|
||||
|
||||
lineListeners.add(onResponseLine);
|
||||
|
||||
try {
|
||||
await sendEggBotCommand(command);
|
||||
} catch (error) {
|
||||
clearTimeout(timeout);
|
||||
lineListeners.delete(onResponseLine);
|
||||
reject(error);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
export function onLine(callback) {
|
||||
lineListeners.add(callback);
|
||||
}
|
||||
|
||||
export function offLine(callback) {
|
||||
lineListeners.delete(callback);
|
||||
}
|
||||
183
docs/examples/eggbot-wifi-client.mjs
Normal file
183
docs/examples/eggbot-wifi-client.mjs
Normal file
@@ -0,0 +1,183 @@
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
let socket = null;
|
||||
let lineBuffer = '';
|
||||
let commandQueue = Promise.resolve();
|
||||
|
||||
const lineListeners = new Set();
|
||||
|
||||
function emitLine(line) {
|
||||
for (const listener of lineListeners) {
|
||||
listener(line);
|
||||
}
|
||||
}
|
||||
|
||||
function parseIncomingChunk(chunk) {
|
||||
lineBuffer += chunk;
|
||||
|
||||
while (true) {
|
||||
const delimiterIndex = lineBuffer.indexOf('\r\n');
|
||||
if (delimiterIndex < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
const line = lineBuffer.slice(0, delimiterIndex);
|
||||
lineBuffer = lineBuffer.slice(delimiterIndex + 2);
|
||||
emitLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSocketMessage(event) {
|
||||
if (typeof event.data === 'string') {
|
||||
parseIncomingChunk(event.data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.data instanceof ArrayBuffer) {
|
||||
parseIncomingChunk(decoder.decode(event.data));
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeCommand(command) {
|
||||
return command.endsWith('\r') ? command : `${command}\r`;
|
||||
}
|
||||
|
||||
function enqueueCommand(fn) {
|
||||
const run = commandQueue.then(fn, fn);
|
||||
commandQueue = run.catch(() => {});
|
||||
return run;
|
||||
}
|
||||
|
||||
function ensureSocketReady() {
|
||||
if (!socket || socket.readyState !== WebSocket.OPEN) {
|
||||
throw new Error('EggDuino WiFi socket is not connected');
|
||||
}
|
||||
}
|
||||
|
||||
function buildSocketUrl(options) {
|
||||
if (options.url) {
|
||||
return options.url;
|
||||
}
|
||||
|
||||
const host = options.host;
|
||||
if (!host) {
|
||||
throw new Error('Missing EggDuino host');
|
||||
}
|
||||
|
||||
const secure = options.secure === true;
|
||||
const protocol = secure ? 'wss' : 'ws';
|
||||
const port = Number(options.port ?? 1337);
|
||||
const path = options.path ?? '/';
|
||||
|
||||
return `${protocol}://${host}:${port}${path}`;
|
||||
}
|
||||
|
||||
export async function connectEggDuinoWifi(options = {}) {
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (socket && socket.readyState === WebSocket.CONNECTING) {
|
||||
await new Promise((resolve, reject) => {
|
||||
const onOpen = () => {
|
||||
socket.removeEventListener('error', onError);
|
||||
resolve();
|
||||
};
|
||||
const onError = () => {
|
||||
socket.removeEventListener('open', onOpen);
|
||||
reject(new Error('EggDuino WiFi socket failed while connecting'));
|
||||
};
|
||||
socket.addEventListener('open', onOpen, { once: true });
|
||||
socket.addEventListener('error', onError, { once: true });
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const url = buildSocketUrl(options);
|
||||
socket = new WebSocket(url);
|
||||
socket.binaryType = 'arraybuffer';
|
||||
|
||||
socket.addEventListener('message', handleSocketMessage);
|
||||
socket.addEventListener('close', () => {
|
||||
socket = null;
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
const onOpen = () => {
|
||||
socket.removeEventListener('error', onError);
|
||||
resolve();
|
||||
};
|
||||
|
||||
const onError = () => {
|
||||
socket.removeEventListener('open', onOpen);
|
||||
socket?.close();
|
||||
socket = null;
|
||||
reject(new Error(`EggDuino WiFi socket connection failed: ${url}`));
|
||||
};
|
||||
|
||||
socket.addEventListener('open', onOpen, { once: true });
|
||||
socket.addEventListener('error', onError, { once: true });
|
||||
});
|
||||
}
|
||||
|
||||
export function disconnectEggDuinoWifi() {
|
||||
if (!socket) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket.removeEventListener('message', handleSocketMessage);
|
||||
if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) {
|
||||
socket.close();
|
||||
}
|
||||
socket = null;
|
||||
}
|
||||
|
||||
export async function sendEggBotCommand(command) {
|
||||
ensureSocketReady();
|
||||
socket.send(normalizeCommand(command));
|
||||
}
|
||||
|
||||
export async function sendEggBotCommandExpectOk(command, timeoutMs = 2000) {
|
||||
return enqueueCommand(() => new Promise(async (resolve, reject) => {
|
||||
const collectedLines = [];
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
lineListeners.delete(onResponseLine);
|
||||
reject(new Error('EggBot response timeout'));
|
||||
}, timeoutMs);
|
||||
|
||||
const onResponseLine = (line) => {
|
||||
collectedLines.push(line);
|
||||
|
||||
if (line === 'OK') {
|
||||
clearTimeout(timeout);
|
||||
lineListeners.delete(onResponseLine);
|
||||
resolve(collectedLines.slice(0, -1));
|
||||
}
|
||||
|
||||
if (line === 'unknown CMD') {
|
||||
clearTimeout(timeout);
|
||||
lineListeners.delete(onResponseLine);
|
||||
reject(new Error(`EggBot error: ${collectedLines.join(' | ')}`));
|
||||
}
|
||||
};
|
||||
|
||||
lineListeners.add(onResponseLine);
|
||||
|
||||
try {
|
||||
await sendEggBotCommand(command);
|
||||
} catch (error) {
|
||||
clearTimeout(timeout);
|
||||
lineListeners.delete(onResponseLine);
|
||||
reject(error);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
export function onLine(callback) {
|
||||
lineListeners.add(callback);
|
||||
}
|
||||
|
||||
export function offLine(callback) {
|
||||
lineListeners.delete(callback);
|
||||
}
|
||||
BIN
docs/res/cnc-shield-mod.png
Normal file
BIN
docs/res/cnc-shield-mod.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 364 KiB |
BIN
firmware/boot_app0.bin
Normal file
BIN
firmware/boot_app0.bin
Normal file
Binary file not shown.
BIN
firmware/bootloader.bin
Normal file
BIN
firmware/bootloader.bin
Normal file
Binary file not shown.
BIN
firmware/firmware.bin
Normal file
BIN
firmware/firmware.bin
Normal file
Binary file not shown.
BIN
firmware/firmware.elf
Executable file
BIN
firmware/firmware.elf
Executable file
Binary file not shown.
131335
firmware/firmware.map
Normal file
131335
firmware/firmware.map
Normal file
File diff suppressed because one or more lines are too long
28
firmware/manifest.json
Normal file
28
firmware/manifest.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "EggDuino ESP32 Firmware",
|
||||
"version": "1.6a",
|
||||
"new_install_prompt_erase": true,
|
||||
"builds": [
|
||||
{
|
||||
"chipFamily": "ESP32",
|
||||
"parts": [
|
||||
{
|
||||
"path": "bootloader.bin",
|
||||
"offset": 4096
|
||||
},
|
||||
{
|
||||
"path": "partitions.bin",
|
||||
"offset": 32768
|
||||
},
|
||||
{
|
||||
"path": "boot_app0.bin",
|
||||
"offset": 57344
|
||||
},
|
||||
{
|
||||
"path": "firmware.bin",
|
||||
"offset": 65536
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
firmware/partitions.bin
Normal file
BIN
firmware/partitions.bin
Normal file
Binary file not shown.
14
include/ArduinoEsp32Compat.h
Normal file
14
include/ArduinoEsp32Compat.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
// Arduino-ESP32 1.0.6 does not define these version macros, but some newer
|
||||
// libraries use them to select legacy-compatible code paths.
|
||||
#ifndef ESP_ARDUINO_VERSION
|
||||
#define EGGDUINO_LEGACY_ARDUINO_ESP32 1
|
||||
#define ESP_ARDUINO_VERSION 0
|
||||
#else
|
||||
#define EGGDUINO_LEGACY_ARDUINO_ESP32 0
|
||||
#endif
|
||||
|
||||
#ifndef ESP_ARDUINO_VERSION_VAL
|
||||
#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) (((major) * 10000) + ((minor) * 100) + (patch))
|
||||
#endif
|
||||
179
include/EggDuino.h
Normal file
179
include/EggDuino.h
Normal file
@@ -0,0 +1,179 @@
|
||||
#ifndef EGGDUINO_H
|
||||
#define EGGDUINO_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#define SUPPRESS_HPP_WARNING
|
||||
#include <ServoEasing.h>
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#include <WebServer.h>
|
||||
#include <SPIFFS.h>
|
||||
#endif
|
||||
|
||||
#include <FastAccelStepper.h>
|
||||
#include "SerialCommand.h"
|
||||
#include "button.h"
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
// Rotational Stepper
|
||||
static const int kDefaultRotStepPin = 25;
|
||||
static const int kDefaultRotDirPin = 27;
|
||||
static const int kDefaultRotEnablePin = 12;
|
||||
static const int kDefaultRotMicrostep = 32;
|
||||
|
||||
// Pen Stepper
|
||||
static const int kDefaultPenStepPin = 25;
|
||||
static const int kDefaultPenDirPin = 16;
|
||||
static const int kDefaultPenEnablePin = 12;
|
||||
static const int kDefaultPenMicrostep = 32;
|
||||
|
||||
// Servo Pin
|
||||
static const int kDefaultServoPin = 17;
|
||||
|
||||
|
||||
#else
|
||||
|
||||
// Rotational Stepper
|
||||
static const int kDefaultRotStepPin = 2;
|
||||
static const int kDefaultRotDirPin = 5;
|
||||
static const int kDefaultRotEnablePin = 8;
|
||||
#define rotMicrostep 16
|
||||
static const int kDefaultRotMicrostep = rotMicrostep;
|
||||
|
||||
// Pen Stepper
|
||||
static const int kDefaultPenStepPin = 3;
|
||||
static const int kDefaultPenDirPin = 6;
|
||||
static const int kDefaultPenEnablePin = 8;
|
||||
#define penMicrostep 16
|
||||
static const int kDefaultPenMicrostep = penMicrostep;
|
||||
|
||||
static const int kDefaultServoPin = 4;
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
struct ConfigParameter {
|
||||
const char *type;
|
||||
const char *key;
|
||||
void *value;
|
||||
String description;
|
||||
int defaultValue;
|
||||
const char *defaultText;
|
||||
bool secret;
|
||||
};
|
||||
|
||||
extern FastAccelStepperEngine g_stepEngine;
|
||||
extern FastAccelStepper *g_pStepperRotate;
|
||||
extern FastAccelStepper *g_pStepperPen;
|
||||
|
||||
extern ServoEasing penServo;
|
||||
extern SerialCommand SCmd;
|
||||
#ifdef ESP32
|
||||
extern SerialCommand g_BLECmd;
|
||||
extern SerialCommand g_WifiCmd;
|
||||
#endif
|
||||
|
||||
extern int g_iPenUpPos;
|
||||
extern int g_iPenDownPos;
|
||||
extern int g_iServoRateUp;
|
||||
extern int g_iServoRateDown;
|
||||
extern long g_iRotStepError;
|
||||
extern long g_iPenStepError;
|
||||
extern int g_iPenState;
|
||||
extern uint32_t g_uiNodeCount;
|
||||
extern unsigned int g_uiLayer;
|
||||
extern boolean g_bPrgButtonState;
|
||||
extern float fROT_STEP_CORRECTION;
|
||||
extern float fPEN_STEP_CORRECTION;
|
||||
extern boolean g_bMotorsEnabled;
|
||||
extern int g_iRotDirPin;
|
||||
extern int g_iRotEnablePin;
|
||||
extern int g_iRotStepPin;
|
||||
extern int g_iPenStepPin;
|
||||
extern int g_iPenDirPin;
|
||||
extern int g_iPenEnablePin;
|
||||
extern int g_iServoPin;
|
||||
extern int g_iRotMicrostep;
|
||||
extern int g_iPenMicrostep;
|
||||
extern int g_iMaxAcclSpeed;
|
||||
extern String g_sHostname;
|
||||
extern String g_sWifiSsid;
|
||||
extern String g_sWifiPassword;
|
||||
extern String g_strConnectionState;
|
||||
|
||||
enum ProtocolTransport {
|
||||
PROTOCOL_TRANSPORT_SERIAL = 0,
|
||||
PROTOCOL_TRANSPORT_BLE = 1,
|
||||
PROTOCOL_TRANSPORT_WIFI = 2,
|
||||
};
|
||||
|
||||
extern ConfigParameter configParameters[];
|
||||
extern const size_t configParameterCount;
|
||||
|
||||
void makeComInterface();
|
||||
void setActiveProtocolContext(SerialCommand *parser, ProtocolTransport transport);
|
||||
char *nextCommandArg();
|
||||
void protocolWrite(const char *message);
|
||||
void protocolWrite(const String &message);
|
||||
void initHardware();
|
||||
void moveOneStep();
|
||||
void moveToDestination();
|
||||
void sendAck();
|
||||
void sendError();
|
||||
void motorsOff();
|
||||
void motorsOn();
|
||||
void toggleMotors();
|
||||
void doTogglePen();
|
||||
void setprgButtonState();
|
||||
bool parseSMArgs(uint16_t *duration, int *penStepsEBB, int *rotStepsEBB);
|
||||
void prepareMove(uint16_t duration, int penStepsEBB, int rotStepsEBB);
|
||||
void storePenUpPosInEE();
|
||||
void storePenDownPosInEE();
|
||||
void updateStepCorrectionFactors();
|
||||
void movePenServoTo(int targetPos);
|
||||
|
||||
bool initConfigStore();
|
||||
bool loadConfigFromFile();
|
||||
bool saveConfigToFile();
|
||||
String buildConfigJson();
|
||||
bool applyConfigJson(const String &payload, String &errorMessage);
|
||||
void startWebInterface();
|
||||
void handleWebInterface();
|
||||
#ifdef ESP32
|
||||
void startBleInterface();
|
||||
void handleBleInterface();
|
||||
bool bleProtocolWrite(const char *message);
|
||||
void buildDeviceName(char *nameBuffer, size_t bufferSize);
|
||||
void startWifiProtocolInterface();
|
||||
void handleWifiProtocolInterface();
|
||||
bool wifiProtocolWrite(const char *message);
|
||||
#else
|
||||
inline void startBleInterface() {}
|
||||
inline void handleBleInterface() {}
|
||||
inline bool bleProtocolWrite(const char *message)
|
||||
{
|
||||
(void)message;
|
||||
return false;
|
||||
}
|
||||
inline void buildDeviceName(char *nameBuffer, size_t bufferSize)
|
||||
{
|
||||
(void)nameBuffer;
|
||||
(void)bufferSize;
|
||||
}
|
||||
inline void startWifiProtocolInterface() {}
|
||||
inline void handleWifiProtocolInterface() {}
|
||||
inline bool wifiProtocolWrite(const char *message)
|
||||
{
|
||||
(void)message;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
void Log(const String &message);
|
||||
void Log(const char *message);
|
||||
String buildLogsJson(uint32_t sinceSeq);
|
||||
void captureIncomingPrintLine(const char *line, ProtocolTransport transport);
|
||||
String buildIncomingPrintLogText();
|
||||
void clearIncomingPrintLog();
|
||||
|
||||
#endif
|
||||
@@ -29,7 +29,7 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
Button(byte p, ActionCb a): debounce(0), state(1), lastState(1), action(a), pin(p) {
|
||||
Button(byte p, ActionCb a): debounce(0), state(1), lastState(1), pin(p), action(a) {
|
||||
pinMode(pin, INPUT_PULLUP);
|
||||
}
|
||||
|
||||
@@ -56,4 +56,3 @@ public:
|
||||
}; //button
|
||||
|
||||
#endif //__BUTTON_H__
|
||||
|
||||
51
include/motion_planner.h
Normal file
51
include/motion_planner.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <Arduino.h>
|
||||
#include <FastAccelStepper.h>
|
||||
|
||||
class XYMotionPlanner {
|
||||
public:
|
||||
XYMotionPlanner(FastAccelStepper* stepperX, FastAccelStepper* stepperY);
|
||||
|
||||
// Basisparameter der "dominanten" Achse
|
||||
void setBaseLimits(uint32_t maxSpeedHz, uint32_t maxAccelStepsPerS2);
|
||||
|
||||
// Minimale Werte vermeiden, dass eine kurze Achse mit 0 endet
|
||||
void setMinimums(uint32_t minSpeedHz, uint32_t minAccelStepsPerS2);
|
||||
|
||||
// Aktuelle Positionen der Planner-Sicht setzen, z.B. nach Homing
|
||||
void setCurrentPosition(int32_t x, int32_t y);
|
||||
|
||||
int32_t currentX() const;
|
||||
int32_t currentY() const;
|
||||
|
||||
bool isRunning() const;
|
||||
|
||||
// Optional blockierend warten
|
||||
void waitUntilFinished();
|
||||
|
||||
// Startet eine koordinierte lineare P2P-Bewegung
|
||||
// Rückgabe false = nichts zu tun oder ungültig
|
||||
bool moveTo(int32_t targetX, int32_t targetY);
|
||||
|
||||
// Convenience: relative Bewegung
|
||||
bool moveBy(int32_t deltaX, int32_t deltaY);
|
||||
|
||||
// Muss regelmäßig aufgerufen werden, wenn du die Planner-Position
|
||||
// nach abgeschlossener Bewegung sauber nachführen willst
|
||||
void update();
|
||||
|
||||
private:
|
||||
FastAccelStepper* m_x = nullptr;
|
||||
FastAccelStepper* m_y = nullptr;
|
||||
|
||||
int32_t m_currentX = 0;
|
||||
int32_t m_currentY = 0;
|
||||
int32_t m_targetX = 0;
|
||||
int32_t m_targetY = 0;
|
||||
|
||||
uint32_t m_baseSpeedHz = 2000;
|
||||
uint32_t m_baseAccel = 4000;
|
||||
uint32_t m_minSpeedHz = 1;
|
||||
uint32_t m_minAccel = 1;
|
||||
|
||||
static uint32_t scaledValue(uint32_t base, float scale, uint32_t distance);
|
||||
};
|
||||
5
inkscape/README.md
Normal file
5
inkscape/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Inkscape
|
||||
Das Verzeichnis ernthält die funktionierenden AxiDraw Plugins für Inkscape. Nach der Installation müssen allerdings noch zwei Datei gepatched werden.
|
||||
1. Installationsort des Inkscape Plugins öffnen. Diese findet man indem man Inkscape öffnet und in Bearbeiten->Einstellung unter System den Eintrag Benutzererweiterungen sucht.
|
||||
2. Im Ordner die Datei eggbot.py durch die im Ordner Patch enthaltene Datei ersetzen.
|
||||
3. Im Ordner des Plugins unter axidraw_deps/plotink die Datei ebb_serial.py ersetzen.
|
||||
15
inkscape/killegg.sh
Executable file
15
inkscape/killegg.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
# killegg.sh
|
||||
# Simple helper script to terminate any running instance of the Python program "eggbot.py" which is started by inkscape.
|
||||
# It searches for the process by name and sends a termination signal.
|
||||
|
||||
PROCESS="eggbot.py"
|
||||
|
||||
PIDS=$(pgrep -f "$PROCESS")
|
||||
|
||||
if [ -z "$PIDS" ]; then
|
||||
echo "Kein laufender Prozess für $PROCESS gefunden."
|
||||
else
|
||||
echo "Beende Prozess(e): $PIDS"
|
||||
kill $PIDS
|
||||
fi
|
||||
BIN
inkscape/linux/AxiDraw_395_LinX86.zip
Normal file
BIN
inkscape/linux/AxiDraw_395_LinX86.zip
Normal file
Binary file not shown.
6
inkscape/linux/readme.md
Normal file
6
inkscape/linux/readme.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Inkscape Plugin
|
||||
This directory contains the plugin that fits to the EggBot firmware.
|
||||
Use the ebb_serial.py file to patch the plugin so that all /dev/ttyUSBx devices can be used.
|
||||
Currently only the first found tty USB device will be used.
|
||||
|
||||
If plugin doesn't work because of missing python dependencies, just install them with apt or similar system tool.
|
||||
457
inkscape/patch/ebb_serial.py
Normal file
457
inkscape/patch/ebb_serial.py
Normal file
@@ -0,0 +1,457 @@
|
||||
# coding=utf-8
|
||||
'''
|
||||
ebb_serial.py
|
||||
Serial connection utilities for EiBotBoard
|
||||
https://github.com/evil-mad/plotink
|
||||
|
||||
Intended to provide some common interfaces that can be used by
|
||||
EggBot, WaterColorBot, AxiDraw, and similar machines.
|
||||
|
||||
See below for version information
|
||||
|
||||
Thanks to Shel Michaels for bug fixes and helpful suggestions.
|
||||
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2022 Windell H. Oskay, Evil Mad Scientist Laboratories
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
'''
|
||||
|
||||
import logging
|
||||
from packaging.version import parse
|
||||
|
||||
from .plot_utils_import import from_dependency_import
|
||||
inkex = from_dependency_import('ink_extensions.inkex')
|
||||
serial = from_dependency_import('serial')
|
||||
from serial.tools.list_ports import comports \
|
||||
#pylint: disable=wrong-import-position, wrong-import-order
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def version():
|
||||
'''Version number for this document'''
|
||||
return "0.19" # Dated 2022-10-05
|
||||
|
||||
|
||||
def findPort():
|
||||
'''
|
||||
Find first available EiBotBoard by searching USB ports. Return serial port object.
|
||||
'''
|
||||
try:
|
||||
com_ports_list = list(comports())
|
||||
except TypeError:
|
||||
return None
|
||||
ebb_port = None
|
||||
for port in com_ports_list:
|
||||
if port[0].startswith("/dev/ttyUSB"):
|
||||
ebb_port = port[0] # Success; EBB found by name match.
|
||||
break # stop searching-- we are done.
|
||||
elif port[0].startswith("COM"):
|
||||
ebb_port = port[0] # Success; EBB found by name match.
|
||||
break # stop searching-- we are done.
|
||||
for port in com_ports_list:
|
||||
if port[1].startswith("EiBotBoard"):
|
||||
ebb_port = port[0] # Success; EBB found by name match.
|
||||
break # stop searching-- we are done.
|
||||
if ebb_port is None:
|
||||
for port in com_ports_list:
|
||||
if port[2].startswith("USB VID:PID=04D8:FD92"):
|
||||
ebb_port = port[0] # Success; EBB found by VID/PID match.
|
||||
break # stop searching-- we are done.
|
||||
return ebb_port
|
||||
|
||||
|
||||
def find_named_ebb(port_name):
|
||||
'''
|
||||
Find a specific EiBotBoard identified by a string giving either:
|
||||
The enumerated serial port, or
|
||||
An EBB "Name tag"
|
||||
Names should be 3-16 characters long. Comparisons are not case sensitive.
|
||||
(Name tags may assigned with the ST command on firmware 2.5.5 and later.)
|
||||
If found: Return serial port name (enumeration)
|
||||
If not found, Return None
|
||||
'''
|
||||
if port_name is not None:
|
||||
needle = 'SER=' + port_name # pyserial 3
|
||||
needle2 = 'SNR=' + port_name # pyserial 2.7
|
||||
needle3 = '(' + port_name + ')' # e.g., "(COM4)"
|
||||
|
||||
needle = needle.lower()
|
||||
needle2 = needle2.lower()
|
||||
needle3 = needle3.lower()
|
||||
plower = port_name.lower()
|
||||
|
||||
try:
|
||||
com_ports_list = list(comports())
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
for port in com_ports_list:
|
||||
p_0 = port[0].lower()
|
||||
p_1 = port[1].lower()
|
||||
p_2 = port[2].lower()
|
||||
|
||||
if needle in p_2:
|
||||
return port[0] # Success; EBB found by name match.
|
||||
if needle2 in p_2:
|
||||
return port[0] # Success; EBB found by name match.
|
||||
if needle3 in p_1:
|
||||
return port[0] # Success; EBB found by port match.
|
||||
|
||||
p_1 = p_1[11:]
|
||||
if p_1.startswith(plower):
|
||||
return port[0] # Success; EBB found by name match.
|
||||
if p_0.startswith(plower):
|
||||
return port[0] # Success; EBB found by port match.
|
||||
|
||||
needle.replace(" ", "_") # SN on Windows has underscores, not spaces.
|
||||
if needle in p_2:
|
||||
return port[0] # Success; EBB found by port match.
|
||||
|
||||
needle2.replace(" ", "_") # SN on Windows has underscores, not spaces.
|
||||
if needle2 in p_2:
|
||||
return port[0] # Success; EBB found by port match.
|
||||
return None
|
||||
|
||||
|
||||
def query_nickname(port_name, verbose=True):
|
||||
'''
|
||||
Query the EBB nickname and report it.
|
||||
If verbose is True or omitted, the result will be human readable.
|
||||
A short version is returned if verbose is False.
|
||||
Requires firmware version 2.5.5 or newer. http://evil-mad.github.io/EggBot/ebb.html#QT
|
||||
'''
|
||||
if port_name is not None:
|
||||
version_status = min_version(port_name, "2.5.5")
|
||||
|
||||
if version_status:
|
||||
raw_string = (query(port_name, 'QT\r'))
|
||||
if raw_string.isspace():
|
||||
if verbose:
|
||||
return "This AxiDraw does not have a nickname assigned."
|
||||
return None
|
||||
if verbose:
|
||||
return "AxiDraw nickname: " + raw_string
|
||||
return str(raw_string).strip()
|
||||
if version_status is False:
|
||||
if verbose:
|
||||
return "AxiDraw naming requires firmware version 2.5.5 or higher."
|
||||
return None
|
||||
|
||||
|
||||
def write_nickname(port_name, nickname):
|
||||
'''
|
||||
Write the EBB nickname.
|
||||
Requires firmware version 2.5.5 or newer. http://evil-mad.github.io/EggBot/ebb.html#ST
|
||||
'''
|
||||
if port_name is not None:
|
||||
version_status = min_version(port_name, "2.5.5")
|
||||
|
||||
if version_status:
|
||||
try:
|
||||
cmd = 'ST,' + nickname + '\r'
|
||||
command(port_name,cmd)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
return None
|
||||
|
||||
|
||||
def reboot(port_name):
|
||||
'''
|
||||
Reboot the EBB, as though it were just powered on.
|
||||
Requires firmware version 2.5.5 or newer. http://evil-mad.github.io/EggBot/ebb.html#RB
|
||||
'''
|
||||
if port_name is not None:
|
||||
version_status = min_version(port_name, "2.5.5")
|
||||
if version_status:
|
||||
try:
|
||||
command(port_name,'RB\r')
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def list_port_info():
|
||||
'''Find and return a list of all USB devices and their information.'''
|
||||
try:
|
||||
com_ports_list = list(comports())
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
port_info_list = []
|
||||
for port in com_ports_list:
|
||||
port_info_list.append(port[0]) # port name
|
||||
port_info_list.append(port[1]) # Identifier
|
||||
port_info_list.append(port[2]) # VID/PID
|
||||
if port_info_list:
|
||||
return port_info_list
|
||||
return None
|
||||
|
||||
|
||||
def listEBBports():
|
||||
'''Find and return a list of all EiBotBoard units connected via USB port.'''
|
||||
|
||||
try:
|
||||
com_ports_list = list(comports())
|
||||
except TypeError:
|
||||
return None
|
||||
ebb_ports_list = []
|
||||
for port in com_ports_list:
|
||||
port_has_ebb = False
|
||||
if port[0].startswith("/dev/ttyUSB"):
|
||||
port_has_ebb = True
|
||||
elif port[0].startswith("COM"):
|
||||
port_has_ebb = True
|
||||
elif port[1].startswith("EiBotBoard"):
|
||||
port_has_ebb = True
|
||||
elif port[2].startswith("USB VID:PID=04D8:FD92"):
|
||||
port_has_ebb = True
|
||||
if port_has_ebb:
|
||||
ebb_ports_list.append(port)
|
||||
if ebb_ports_list:
|
||||
return ebb_ports_list
|
||||
return None
|
||||
|
||||
|
||||
def list_named_ebbs():
|
||||
'''Return descriptive list of all EiBotBoard units'''
|
||||
ebb_ports_list = listEBBports()
|
||||
if not ebb_ports_list:
|
||||
return None
|
||||
ebb_names_list = []
|
||||
for port in ebb_ports_list:
|
||||
name_found = False
|
||||
p_0 = port[0]
|
||||
p_1 = port[1]
|
||||
p_2 = port[2]
|
||||
if p_1.startswith("EiBotBoard"):
|
||||
temp_string = p_1[11:]
|
||||
if temp_string:
|
||||
if temp_string is not None:
|
||||
ebb_names_list.append(temp_string)
|
||||
name_found = True
|
||||
if not name_found:
|
||||
# Look for "SER=XXXX LOCAT" pattern,
|
||||
# typical of Pyserial 3 on Windows.
|
||||
if 'SER=' in p_2 and ' LOCAT' in p_2:
|
||||
index1 = p_2.find('SER=') + len('SER=')
|
||||
index2 = p_2.find(' LOCAT', index1)
|
||||
temp_string = p_2[index1:index2]
|
||||
if len(temp_string) < 3:
|
||||
temp_string = None
|
||||
if temp_string is not None:
|
||||
ebb_names_list.append(temp_string)
|
||||
name_found = True
|
||||
if not name_found:
|
||||
# Look for "...SNR=XXXX" pattern,
|
||||
# typical of Pyserial 2.7 on Windows
|
||||
if 'SNR=' in p_2:
|
||||
index1 = p_2.find('SNR=') + len('SNR=')
|
||||
index2 = len(p_2)
|
||||
temp_string = p_2[index1:index2]
|
||||
if len(temp_string) < 3:
|
||||
temp_string = None
|
||||
if temp_string is not None:
|
||||
ebb_names_list.append(temp_string)
|
||||
name_found = True
|
||||
if not name_found:
|
||||
ebb_names_list.append(p_0)
|
||||
return ebb_names_list
|
||||
|
||||
|
||||
def testPort(port_name):
|
||||
"""
|
||||
Open a given serial port, verify that it is an EiBotBoard,
|
||||
and return a SerialPort object that we can reference later.
|
||||
|
||||
This routine only opens the port; it will need to be closed as well,
|
||||
for example with closePort( port_name ).
|
||||
You, who open the port, are responsible for closing it as well.
|
||||
"""
|
||||
if port_name is not None:
|
||||
try:
|
||||
serial_port = serial.Serial(port_name, timeout=1.0, baudrate=115200) # 1 second timeout!
|
||||
|
||||
serial_port.flushInput() # deprecated function name;
|
||||
# use serial_port.reset_input_buffer()
|
||||
# if we can be sure that we have pySerial 3+.
|
||||
|
||||
serial_port.write('v\r'.encode('ascii'))
|
||||
str_version = serial_port.readline()
|
||||
if str_version and str_version.startswith("EBB".encode('ascii')):
|
||||
return serial_port
|
||||
|
||||
serial_port.write('v\r'.encode('ascii'))
|
||||
str_version = serial_port.readline()
|
||||
if str_version and str_version.startswith("EBB".encode('ascii')):
|
||||
return serial_port
|
||||
serial_port.close()
|
||||
except serial.SerialException as err:
|
||||
logger.error("Error testing serial port `{}` connection".format(port_name))
|
||||
logger.info("Error context:", exc_info=err)
|
||||
return None
|
||||
|
||||
|
||||
def openPort():
|
||||
'''
|
||||
Find and open a port to a single attached EiBotBoard.
|
||||
The first port located will be used.
|
||||
'''
|
||||
found_port = findPort()
|
||||
serial_port = testPort(found_port)
|
||||
if serial_port:
|
||||
return serial_port
|
||||
return None
|
||||
|
||||
|
||||
def open_named_port(port_name):
|
||||
'''
|
||||
Find and open a port to a single attached EiBotBoard, indicated by name.
|
||||
The first port located will be used.
|
||||
'''
|
||||
found_port = find_named_ebb(port_name)
|
||||
serial_port = testPort(found_port)
|
||||
if serial_port:
|
||||
return serial_port
|
||||
return None
|
||||
|
||||
|
||||
def closePort(port_name):
|
||||
'''Close the given serial port.'''
|
||||
if port_name is not None:
|
||||
try:
|
||||
port_name.close()
|
||||
except serial.SerialException:
|
||||
pass
|
||||
|
||||
|
||||
def query(port_name, cmd, verbose=True):
|
||||
'''General command to send a query to the EiBotBoard'''
|
||||
if port_name is not None and cmd is not None:
|
||||
response = ''
|
||||
try:
|
||||
port_name.write(cmd.encode('ascii'))
|
||||
response = port_name.readline().decode('ascii')
|
||||
n_retry_count = 0
|
||||
while len(response) == 0 and n_retry_count < 100:
|
||||
# get new response to replace null response if necessary
|
||||
response = port_name.readline()
|
||||
n_retry_count += 1
|
||||
if cmd.split(",")[0].strip().lower() not in ["a", "i", "mr", "pi", "qm", "qg", "v"]:
|
||||
# Most queries return an "OK" after the data requested.
|
||||
# We skip this for those few queries that do not return an extra line.
|
||||
unused_response = port_name.readline() # read in extra blank/OK line
|
||||
n_retry_count = 0
|
||||
while len(unused_response) == 0 and n_retry_count < 100:
|
||||
# get new response to replace null response if necessary
|
||||
unused_response = port_name.readline()
|
||||
n_retry_count += 1
|
||||
except (serial.SerialException, IOError, RuntimeError, OSError) as err:
|
||||
if verbose:
|
||||
logger.error("Error reading serial data")
|
||||
else:
|
||||
logger.info("Error reading serial data")
|
||||
logger.info("Error context:", exc_info=err)
|
||||
|
||||
if 'Err:' in response:
|
||||
error_msg = '\n'.join(('Unexpected response from EBB.',
|
||||
' Command: {0}'.format(cmd.strip()),
|
||||
' Response: {0}'.format(response.strip())))
|
||||
if verbose:
|
||||
logger.error(error_msg)
|
||||
else:
|
||||
logger.info(error_msg)
|
||||
return response
|
||||
return None
|
||||
|
||||
|
||||
def command(port_name, cmd, verbose=True):
|
||||
'''General command to send a command to the EiBotBoard'''
|
||||
if port_name is not None and cmd is not None:
|
||||
try:
|
||||
port_name.write(cmd.encode('ascii'))
|
||||
response = port_name.readline().decode('ascii')
|
||||
n_retry_count = 0
|
||||
while len(response) == 0 and n_retry_count < 100:
|
||||
# get new response to replace null response if necessary
|
||||
response = port_name.readline().decode('ascii')
|
||||
n_retry_count += 1
|
||||
if response.strip().startswith("OK"):
|
||||
# Debug option: indicate which command:
|
||||
# inkex.errormsg( 'OK after command: ' + cmd )
|
||||
pass
|
||||
else:
|
||||
if response:
|
||||
error_msg = '\n'.join(('Unexpected response from EBB.',
|
||||
' Command: {0}'.format(cmd.strip()),
|
||||
' Response: {0}'.format(response.strip())))
|
||||
else:
|
||||
error_msg = 'EBB Serial Timeout after command: {0}'.format(cmd)
|
||||
if verbose:
|
||||
logger.error(error_msg)
|
||||
else:
|
||||
logger.info(error_msg)
|
||||
except (serial.SerialException, IOError, RuntimeError, OSError) as err:
|
||||
if cmd.strip().lower() not in ["rb"]: # Ignore error on reboot (RB) command
|
||||
if verbose:
|
||||
logger.error('Failed after command: {0}'.format(cmd))
|
||||
else:
|
||||
logger.info('Failed after command: {0}'.format(cmd))
|
||||
logger.info("Error context:", exc_info=err)
|
||||
|
||||
|
||||
def bootload(port_name):
|
||||
'''Enter bootloader mode. Do not try to read back data.'''
|
||||
if port_name is not None:
|
||||
try:
|
||||
port_name.write('BL\r'.encode('ascii'))
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
return None
|
||||
|
||||
|
||||
def min_version(port_name, version_string):
|
||||
'''
|
||||
Query the EBB firmware version for the EBB located at port_name.
|
||||
Return True if the EBB firmware version is at least version_string.
|
||||
Return False if the EBB firmware version is below version_string.
|
||||
Return None if we are unable to determine True or False.
|
||||
'''
|
||||
if port_name is not None:
|
||||
ebb_version_string = queryVersion(port_name) # Full string, human readable
|
||||
ebb_version_string = ebb_version_string.split("Firmware Version ", 1)
|
||||
|
||||
if len(ebb_version_string) > 1:
|
||||
ebb_version_string = ebb_version_string[1]
|
||||
else:
|
||||
return None # We haven't received a reasonable version number response.
|
||||
|
||||
ebb_version_string = ebb_version_string.strip() # Stripped copy, for number comparisons
|
||||
if parse(ebb_version_string) >= parse(version_string):
|
||||
return True
|
||||
return False
|
||||
return None
|
||||
|
||||
|
||||
def queryVersion(port_name):
|
||||
'''Query EBB Version String'''
|
||||
return query(port_name, 'V\r', True)
|
||||
1181
inkscape/patch/eggbot.py
Executable file
1181
inkscape/patch/eggbot.py
Executable file
File diff suppressed because it is too large
Load Diff
BIN
inkscape/windows/AxiDraw_394_Win.exe.zip
Normal file
BIN
inkscape/windows/AxiDraw_394_Win.exe.zip
Normal file
Binary file not shown.
@@ -30,6 +30,7 @@ SerialCommand::SerialCommand()
|
||||
: commandList(NULL),
|
||||
commandCount(0),
|
||||
defaultHandler(NULL),
|
||||
lineHandler(NULL),
|
||||
term('\r'), // default terminator for commands, newline character
|
||||
last(NULL)
|
||||
{
|
||||
@@ -64,6 +65,10 @@ void SerialCommand::setDefaultHandler(void (*function)(const char *)) {
|
||||
defaultHandler = function;
|
||||
}
|
||||
|
||||
void SerialCommand::setLineHandler(LineHandler function) {
|
||||
lineHandler = function;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This checks the Serial stream for characters, and assembles them into a buffer.
|
||||
@@ -71,58 +76,69 @@ void SerialCommand::setDefaultHandler(void (*function)(const char *)) {
|
||||
* buffer for a prefix command, and calls handlers setup by addCommand() member
|
||||
*/
|
||||
void SerialCommand::readSerial() {
|
||||
while (Serial.available() > 0) {
|
||||
char inChar = Serial.read(); // Read single available character, there may be more waiting
|
||||
readSerial(Serial);
|
||||
}
|
||||
|
||||
void SerialCommand::readSerial(Stream &stream) {
|
||||
while (stream.available() > 0) {
|
||||
char inChar = stream.read(); // Read single available character, there may be more waiting
|
||||
readChar(inChar);
|
||||
}
|
||||
}
|
||||
|
||||
void SerialCommand::readChar(char inChar) {
|
||||
#ifdef SERIALCOMMAND_DEBUG
|
||||
Serial.print(inChar); // Echo back to serial stream
|
||||
#endif
|
||||
|
||||
if (inChar == term) { // Check for the terminator (default '\r') meaning end of command
|
||||
#ifdef SERIALCOMMAND_DEBUG
|
||||
Serial.print(inChar); // Echo back to serial stream
|
||||
Serial.print("Received: ");
|
||||
Serial.println(buffer);
|
||||
#endif
|
||||
|
||||
if (inChar == term) { // Check for the terminator (default '\r') meaning end of command
|
||||
#ifdef SERIALCOMMAND_DEBUG
|
||||
Serial.print("Received: ");
|
||||
Serial.println(buffer);
|
||||
#endif
|
||||
if ((lineHandler != NULL) && (bufPos > 0)) {
|
||||
(*lineHandler)(buffer);
|
||||
}
|
||||
|
||||
char *command = strtok_r(buffer, delim, &last); // Search for command at start of buffer
|
||||
if (command != NULL) {
|
||||
boolean matched = false;
|
||||
for (int i = 0; i < commandCount; i++) {
|
||||
char *command = strtok_r(buffer, delim, &last); // Search for command at start of buffer
|
||||
if (command != NULL) {
|
||||
boolean matched = false;
|
||||
for (int i = 0; i < commandCount; i++) {
|
||||
#ifdef SERIALCOMMAND_DEBUG
|
||||
Serial.print("Comparing [");
|
||||
Serial.print(command);
|
||||
Serial.print("] to [");
|
||||
Serial.print(commandList[i].command);
|
||||
Serial.println("]");
|
||||
#endif
|
||||
|
||||
// Compare the found command against the list of known commands for a match
|
||||
if (strncmp(command, commandList[i].command, SERIALCOMMAND_MAXCOMMANDLENGTH) == 0) {
|
||||
#ifdef SERIALCOMMAND_DEBUG
|
||||
Serial.print("Comparing [");
|
||||
Serial.print(command);
|
||||
Serial.print("] to [");
|
||||
Serial.print(commandList[i].command);
|
||||
Serial.println("]");
|
||||
Serial.print("Matched Command: ");
|
||||
Serial.println(command);
|
||||
#endif
|
||||
|
||||
// Compare the found command against the list of known commands for a match
|
||||
if (strncmp(command, commandList[i].command, SERIALCOMMAND_MAXCOMMANDLENGTH) == 0) {
|
||||
#ifdef SERIALCOMMAND_DEBUG
|
||||
Serial.print("Matched Command: ");
|
||||
Serial.println(command);
|
||||
#endif
|
||||
|
||||
// Execute the stored handler function for the command
|
||||
(*commandList[i].function)();
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched && (defaultHandler != NULL)) {
|
||||
(*defaultHandler)(command);
|
||||
// Execute the stored handler function for the command
|
||||
(*commandList[i].function)();
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
clearBuffer();
|
||||
if (!matched && (defaultHandler != NULL)) {
|
||||
(*defaultHandler)(command);
|
||||
}
|
||||
}
|
||||
else if (isprint(inChar)) { // Only printable characters into the buffer
|
||||
if (bufPos < SERIALCOMMAND_BUFFER) {
|
||||
buffer[bufPos++] = inChar; // Put character into buffer
|
||||
buffer[bufPos] = '\0'; // Null terminate
|
||||
} else {
|
||||
#ifdef SERIALCOMMAND_DEBUG
|
||||
Serial.println("Line buffer is full - increase SERIALCOMMAND_BUFFER");
|
||||
#endif
|
||||
}
|
||||
clearBuffer();
|
||||
} else if (isprint(inChar)) { // Only printable characters into the buffer
|
||||
if (bufPos < SERIALCOMMAND_BUFFER) {
|
||||
buffer[bufPos++] = inChar; // Put character into buffer
|
||||
buffer[bufPos] = '\0'; // Null terminate
|
||||
} else {
|
||||
#ifdef SERIALCOMMAND_DEBUG
|
||||
Serial.println("Line buffer is full - increase SERIALCOMMAND_BUFFER");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,11 +44,16 @@
|
||||
|
||||
class SerialCommand {
|
||||
public:
|
||||
typedef void (*LineHandler)(const char *);
|
||||
|
||||
SerialCommand(); // Constructor
|
||||
void addCommand(const char *command, void(*function)()); // Add a command to the processing dictionary.
|
||||
void setDefaultHandler(void (*function)(const char *)); // A handler to call when no valid command received.
|
||||
void setLineHandler(LineHandler function); // Optional handler called with each completed input line.
|
||||
|
||||
void readSerial(); // Main entry point.
|
||||
void readSerial(Stream &stream); // Reads commands from any Stream-compatible transport.
|
||||
void readChar(char inChar); // Feeds one incoming character into the parser.
|
||||
void clearBuffer(); // Clears the input buffer.
|
||||
char *next(); // Returns pointer to next token found in command buffer (for getting arguments to commands).
|
||||
|
||||
@@ -63,6 +68,7 @@ class SerialCommand {
|
||||
|
||||
// Pointer to the default handler function
|
||||
void (*defaultHandler)(const char *);
|
||||
LineHandler lineHandler;
|
||||
|
||||
char delim[2]; // null-terminated list of character to be used as delimeters for tokenizing (default " ")
|
||||
char term; // Character that signals end of command (default '\n')
|
||||
52
platformio.ini
Normal file
52
platformio.ini
Normal file
@@ -0,0 +1,52 @@
|
||||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs = uno_linux
|
||||
extra_configs = platformio_override.ini
|
||||
|
||||
[env]
|
||||
platform = platformio/espressif32
|
||||
board = esp32dev
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
lib_deps =
|
||||
arminjo/ServoEasing
|
||||
madhephaestus/ESP32Servo@^3.0.6
|
||||
bblanchon/ArduinoJson@^6.21.5
|
||||
links2004/WebSockets@^2.6.1
|
||||
|
||||
[env:uno_linux]
|
||||
upload_speed = 576000
|
||||
upload_port = /dev/ttyUSB*
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
gin66/FastAccelStepper@^0.33.13
|
||||
h2zero/NimBLE-Arduino@^2.3.6
|
||||
|
||||
[env:uno_windows]
|
||||
extra_scripts = pre:scripts/patch_legacy_esp32_libs.py
|
||||
build_flags =
|
||||
-DEGGDUINO_WINDOWS_BUILD_FIXES=1
|
||||
-include $PROJECT_INCLUDE_DIR/ArduinoEsp32Compat.h
|
||||
monitor_port = COM*
|
||||
upload_port = COM8
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
gin66/FastAccelStepper@0.30.15
|
||||
h2zero/NimBLE-Arduino@2.2.3
|
||||
|
||||
[env:uno_macos]
|
||||
monitor_port = /dev/cu.usb*
|
||||
upload_port = /dev/cu.usb*
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
gin66/FastAccelStepper@^0.33.13
|
||||
h2zero/NimBLE-Arduino@^2.3.6
|
||||
5
platformio_override_example.ini
Normal file
5
platformio_override_example.ini
Normal file
@@ -0,0 +1,5 @@
|
||||
# Copy file to platformio_override.ini and change default_envs to your needs.
|
||||
# In this example file it uses the mac configuration.
|
||||
|
||||
[platformio]
|
||||
default_envs = uno_mac
|
||||
12
scripts/check_config_web_stack_usage.sh
Executable file
12
scripts/check_config_web_stack_usage.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
if rg -n '^[[:space:]]+StaticJsonDocument<kConfigJsonCapacity>[[:space:]]+[A-Za-z_][A-Za-z0-9_]*;' src/Config_Web.cpp; then
|
||||
echo "Config_Web.cpp still allocates kConfigJsonCapacity-sized StaticJsonDocument on the stack" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Config_Web.cpp avoids stack-local kConfigJsonCapacity StaticJsonDocument allocations"
|
||||
101
scripts/package_firmware.py
Normal file
101
scripts/package_firmware.py
Normal file
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
BUILD_ARTIFACTS = (
|
||||
"bootloader.bin",
|
||||
"partitions.bin",
|
||||
"firmware.bin",
|
||||
"firmware.elf",
|
||||
"firmware.map",
|
||||
)
|
||||
|
||||
MANIFEST_PARTS = (
|
||||
{"path": "bootloader.bin", "offset": 4096},
|
||||
{"path": "partitions.bin", "offset": 32768},
|
||||
{"path": "boot_app0.bin", "offset": 57344},
|
||||
{"path": "firmware.bin", "offset": 65536},
|
||||
)
|
||||
|
||||
VERSION_PATTERN = re.compile(r'Eggduino-Firmware V([^"\\]+)')
|
||||
|
||||
|
||||
def extract_firmware_version(header_text: str) -> str:
|
||||
match = VERSION_PATTERN.search(header_text)
|
||||
if not match:
|
||||
raise ValueError("Could not extract firmware version from include/EggDuino.h")
|
||||
return match.group(1).strip()
|
||||
|
||||
|
||||
def build_manifest(version: str) -> dict:
|
||||
return {
|
||||
"name": "EggDuino ESP32 Firmware",
|
||||
"version": version,
|
||||
"new_install_prompt_erase": True,
|
||||
"builds": [
|
||||
{
|
||||
"chipFamily": "ESP32",
|
||||
"parts": list(MANIFEST_PARTS),
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def package_bundle(build_dir: Path, boot_app0_path: Path, header_path: Path, output_dir: Path) -> None:
|
||||
build_dir = Path(build_dir)
|
||||
boot_app0_path = Path(boot_app0_path)
|
||||
header_path = Path(header_path)
|
||||
output_dir = Path(output_dir)
|
||||
|
||||
if output_dir.exists():
|
||||
shutil.rmtree(output_dir)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for artifact_name in BUILD_ARTIFACTS:
|
||||
artifact_path = build_dir / artifact_name
|
||||
if not artifact_path.is_file():
|
||||
raise FileNotFoundError(f"Missing build artifact: {artifact_path}")
|
||||
shutil.copy2(artifact_path, output_dir / artifact_name)
|
||||
|
||||
if not boot_app0_path.is_file():
|
||||
raise FileNotFoundError(f"Missing boot_app0.bin: {boot_app0_path}")
|
||||
shutil.copy2(boot_app0_path, output_dir / "boot_app0.bin")
|
||||
|
||||
version = extract_firmware_version(header_path.read_text(encoding="utf-8"))
|
||||
manifest_path = output_dir / "manifest.json"
|
||||
manifest_path.write_text(
|
||||
json.dumps(build_manifest(version), indent=4) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
||||
def _get_boot_app0_path(env) -> Path:
|
||||
framework_dir = env.PioPlatform().get_package_dir("framework-arduinoespressif32")
|
||||
if not framework_dir:
|
||||
raise FileNotFoundError("PlatformIO framework-arduinoespressif32 package is not installed")
|
||||
|
||||
boot_app0_path = Path(framework_dir) / "tools" / "partitions" / "boot_app0.bin"
|
||||
if not boot_app0_path.is_file():
|
||||
raise FileNotFoundError(f"Could not find boot_app0.bin in {boot_app0_path.parent}")
|
||||
return boot_app0_path
|
||||
|
||||
|
||||
def _package_platformio_bundle(target, source, env) -> None:
|
||||
del target, source
|
||||
project_dir = Path(env.subst("$PROJECT_DIR"))
|
||||
package_bundle(
|
||||
build_dir=Path(env.subst("$BUILD_DIR")),
|
||||
boot_app0_path=_get_boot_app0_path(env),
|
||||
header_path=project_dir / "include" / "EggDuino.h",
|
||||
output_dir=project_dir / "firmware",
|
||||
)
|
||||
print(f"Packaged firmware bundle to {project_dir / 'firmware'}")
|
||||
|
||||
|
||||
if "Import" in globals():
|
||||
Import("env")
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", _package_platformio_bundle)
|
||||
98
scripts/patch_legacy_esp32_libs.py
Normal file
98
scripts/patch_legacy_esp32_libs.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from pathlib import Path
|
||||
|
||||
Import("env")
|
||||
|
||||
|
||||
def patch_nimble_address() -> None:
|
||||
libdeps_dir = Path(env.subst("$PROJECT_LIBDEPS_DIR"))
|
||||
env_name = env.subst("$PIOENV")
|
||||
source_path = libdeps_dir / env_name / "NimBLE-Arduino" / "src" / "NimBLEAddress.cpp"
|
||||
|
||||
if not source_path.exists():
|
||||
return
|
||||
|
||||
original = source_path.read_text(encoding="utf-8")
|
||||
updated = original
|
||||
|
||||
include_old = '# include <algorithm>\n'
|
||||
include_new = '# include <algorithm>\n# include <cstdlib>\n'
|
||||
if include_old in updated and "# include <cstdlib>\n" not in updated:
|
||||
updated = updated.replace(include_old, include_new, 1)
|
||||
|
||||
call_old = " uint64_t address = std::stoull(mac, nullptr, 16);"
|
||||
call_new = " uint64_t address = strtoull(mac.c_str(), nullptr, 16);"
|
||||
updated = updated.replace(call_old, call_new, 1)
|
||||
|
||||
if updated != original:
|
||||
source_path.write_text(updated, encoding="utf-8")
|
||||
print("Patched NimBLE-Arduino for legacy ESP32 toolchain compatibility")
|
||||
|
||||
|
||||
def patch_nimble_device() -> None:
|
||||
libdeps_dir = Path(env.subst("$PROJECT_LIBDEPS_DIR"))
|
||||
env_name = env.subst("$PIOENV")
|
||||
source_path = libdeps_dir / env_name / "NimBLE-Arduino" / "src" / "NimBLEDevice.cpp"
|
||||
|
||||
if not source_path.exists():
|
||||
return
|
||||
|
||||
original = source_path.read_text(encoding="utf-8")
|
||||
updated = original
|
||||
|
||||
updated = updated.replace(
|
||||
" ble_sm_io pkey{.action = BLE_SM_IOACT_INPUT, .passkey = passkey};\n",
|
||||
" ble_sm_io pkey{};\n"
|
||||
" pkey.action = BLE_SM_IOACT_INPUT;\n"
|
||||
" pkey.passkey = passkey;\n",
|
||||
1,
|
||||
)
|
||||
updated = updated.replace(
|
||||
" ble_sm_io pkey{.action = BLE_SM_IOACT_NUMCMP, .numcmp_accept = accept};\n",
|
||||
" ble_sm_io pkey{};\n"
|
||||
" pkey.action = BLE_SM_IOACT_NUMCMP;\n"
|
||||
" pkey.numcmp_accept = accept;\n",
|
||||
1,
|
||||
)
|
||||
|
||||
if updated != original:
|
||||
source_path.write_text(updated, encoding="utf-8")
|
||||
print("Patched NimBLEDevice.cpp for legacy ESP32 toolchain compatibility")
|
||||
|
||||
|
||||
def patch_websockets_client() -> None:
|
||||
libdeps_dir = Path(env.subst("$PROJECT_LIBDEPS_DIR"))
|
||||
env_name = env.subst("$PIOENV")
|
||||
source_path = libdeps_dir / env_name / "WebSockets" / "src" / "WebSocketsClient.cpp"
|
||||
|
||||
if not source_path.exists():
|
||||
return
|
||||
|
||||
original = source_path.read_text(encoding="utf-8")
|
||||
updated = original
|
||||
|
||||
old = (
|
||||
"#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 4)\n"
|
||||
" _client.ssl->setCACertBundle(_CA_bundle, _CA_bundle_size);\n"
|
||||
"#else\n"
|
||||
" _client.ssl->setCACertBundle(_CA_bundle);\n"
|
||||
"#endif\n"
|
||||
)
|
||||
new = (
|
||||
"#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 4)\n"
|
||||
" _client.ssl->setCACertBundle(_CA_bundle, _CA_bundle_size);\n"
|
||||
"#else\n"
|
||||
" // Arduino-ESP32 1.x has no CA bundle API; the project only uses\n"
|
||||
" // WebSocketsServer, so keep the client path buildable with insecure TLS.\n"
|
||||
" _client.ssl->setInsecure();\n"
|
||||
"#endif\n"
|
||||
)
|
||||
updated = updated.replace(old, new, 1)
|
||||
|
||||
if updated != original:
|
||||
source_path.write_text(updated, encoding="utf-8")
|
||||
print("Patched WebSocketsClient.cpp for legacy ESP32 toolchain compatibility")
|
||||
|
||||
|
||||
patch_nimble_address()
|
||||
patch_nimble_device()
|
||||
patch_websockets_client()
|
||||
231
src/BLE_Interface.cpp
Normal file
231
src/BLE_Interface.cpp
Normal file
@@ -0,0 +1,231 @@
|
||||
#include "EggDuino.h"
|
||||
|
||||
#ifdef ESP32
|
||||
#include <NimBLEDevice.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr char kBleDeviceNamePrefix[] = "EggBot_";
|
||||
constexpr char kBleServiceUuid[] = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
|
||||
constexpr char kBleRxCharUuid[] = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
|
||||
constexpr char kBleTxCharUuid[] = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
|
||||
|
||||
constexpr size_t kBleRxQueueSize = 512;
|
||||
constexpr size_t kBleNotifyChunkSize = 20;
|
||||
|
||||
NimBLEServer *g_pBleServer = NULL;
|
||||
NimBLECharacteristic *g_pBleTxCharacteristic = NULL;
|
||||
|
||||
uint8_t g_bleRxQueue[kBleRxQueueSize];
|
||||
size_t g_bleRxHead = 0;
|
||||
size_t g_bleRxTail = 0;
|
||||
bool g_bleRxQueueOverflow = false;
|
||||
bool g_bleClientConnected = false;
|
||||
|
||||
portMUX_TYPE g_bleQueueMux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
void logBleDiag(const String &message)
|
||||
{
|
||||
Log(message);
|
||||
}
|
||||
|
||||
bool queueBleByte(uint8_t value)
|
||||
{
|
||||
bool queued = false;
|
||||
portENTER_CRITICAL(&g_bleQueueMux);
|
||||
const size_t nextHead = (g_bleRxHead + 1) % kBleRxQueueSize;
|
||||
if (nextHead == g_bleRxTail)
|
||||
{
|
||||
g_bleRxQueueOverflow = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_bleRxQueue[g_bleRxHead] = value;
|
||||
g_bleRxHead = nextHead;
|
||||
queued = true;
|
||||
}
|
||||
portEXIT_CRITICAL(&g_bleQueueMux);
|
||||
return queued;
|
||||
}
|
||||
|
||||
bool dequeueBleByte(uint8_t *value)
|
||||
{
|
||||
bool hasData = false;
|
||||
portENTER_CRITICAL(&g_bleQueueMux);
|
||||
if (g_bleRxHead != g_bleRxTail)
|
||||
{
|
||||
*value = g_bleRxQueue[g_bleRxTail];
|
||||
g_bleRxTail = (g_bleRxTail + 1) % kBleRxQueueSize;
|
||||
hasData = true;
|
||||
}
|
||||
portEXIT_CRITICAL(&g_bleQueueMux);
|
||||
return hasData;
|
||||
}
|
||||
|
||||
class EggDuinoBleServerCallbacks : public NimBLEServerCallbacks
|
||||
{
|
||||
void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo)
|
||||
{
|
||||
(void)pServer;
|
||||
(void)connInfo;
|
||||
g_bleClientConnected = true;
|
||||
Log("BLE client connected");
|
||||
}
|
||||
|
||||
void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason)
|
||||
{
|
||||
(void)connInfo;
|
||||
(void)reason;
|
||||
g_bleClientConnected = false;
|
||||
const bool restartedAdvertising = pServer->startAdvertising();
|
||||
logBleDiag(String("BLE client disconnected; advertising restart: ") + (restartedAdvertising ? "ok" : "failed"));
|
||||
}
|
||||
};
|
||||
|
||||
class EggDuinoBleRxCallbacks : public NimBLECharacteristicCallbacks
|
||||
{
|
||||
void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo)
|
||||
{
|
||||
(void)connInfo;
|
||||
const std::string value = pCharacteristic->getValue();
|
||||
for (size_t i = 0; i < value.size(); ++i)
|
||||
{
|
||||
queueBleByte(static_cast<uint8_t>(value[i]));
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void startBleInterface()
|
||||
{
|
||||
char bleDeviceName[32] = {0};
|
||||
buildDeviceName(bleDeviceName, sizeof(bleDeviceName));
|
||||
if (bleDeviceName[0] == '\0')
|
||||
{
|
||||
snprintf(bleDeviceName, sizeof(bleDeviceName), "%sUNKNOWN", kBleDeviceNamePrefix);
|
||||
}
|
||||
|
||||
logBleDiag("BLE init begin");
|
||||
logBleDiag(String("BLE device name: ") + bleDeviceName);
|
||||
logBleDiag(String("BLE service UUID: ") + kBleServiceUuid);
|
||||
logBleDiag(String("BLE RX UUID: ") + kBleRxCharUuid);
|
||||
logBleDiag(String("BLE TX UUID: ") + kBleTxCharUuid);
|
||||
|
||||
NimBLEDevice::init(bleDeviceName);
|
||||
const bool blePowerSet = NimBLEDevice::setPower(ESP_PWR_LVL_P6);
|
||||
const std::string bleAddress = NimBLEDevice::getAddress().toString();
|
||||
logBleDiag(String("BLE radio address: ") + bleAddress.c_str());
|
||||
logBleDiag(String("BLE TX power set: ") + (blePowerSet ? "ok" : "failed"));
|
||||
|
||||
g_pBleServer = NimBLEDevice::createServer();
|
||||
if (g_pBleServer == NULL)
|
||||
{
|
||||
logBleDiag("BLE init failed: createServer returned null");
|
||||
return;
|
||||
}
|
||||
g_pBleServer->setCallbacks(new EggDuinoBleServerCallbacks());
|
||||
logBleDiag("BLE server created");
|
||||
|
||||
NimBLEService *pService = g_pBleServer->createService(kBleServiceUuid);
|
||||
if (pService == NULL)
|
||||
{
|
||||
logBleDiag("BLE init failed: createService returned null");
|
||||
return;
|
||||
}
|
||||
g_pBleTxCharacteristic = pService->createCharacteristic(
|
||||
kBleTxCharUuid,
|
||||
NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
|
||||
if (g_pBleTxCharacteristic == NULL)
|
||||
{
|
||||
logBleDiag("BLE init failed: TX characteristic creation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
NimBLECharacteristic *pRxCharacteristic = pService->createCharacteristic(
|
||||
kBleRxCharUuid,
|
||||
NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR);
|
||||
if (pRxCharacteristic == NULL)
|
||||
{
|
||||
logBleDiag("BLE init failed: RX characteristic creation failed");
|
||||
return;
|
||||
}
|
||||
pRxCharacteristic->setCallbacks(new EggDuinoBleRxCallbacks());
|
||||
logBleDiag("BLE characteristics created");
|
||||
|
||||
const bool serviceStarted = pService->start();
|
||||
logBleDiag(String("BLE service start: ") + (serviceStarted ? "ok" : "failed"));
|
||||
if (!serviceStarted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
if (pAdvertising == NULL)
|
||||
{
|
||||
logBleDiag("BLE init failed: getAdvertising returned null");
|
||||
return;
|
||||
}
|
||||
pAdvertising->enableScanResponse(true);
|
||||
const bool localNameSet = pAdvertising->setName(bleDeviceName);
|
||||
const bool serviceUuidAdded = pAdvertising->addServiceUUID(kBleServiceUuid);
|
||||
const bool advertisingStarted = pAdvertising->start();
|
||||
logBleDiag(String("BLE advertising set local name: ") + (localNameSet ? "ok" : "failed"));
|
||||
logBleDiag(String("BLE advertising add service UUID: ") + (serviceUuidAdded ? "ok" : "failed"));
|
||||
logBleDiag(String("BLE advertising start: ") + (advertisingStarted ? "ok" : "failed"));
|
||||
if (serviceUuidAdded && advertisingStarted)
|
||||
{
|
||||
logBleDiag("BLE service started");
|
||||
}
|
||||
}
|
||||
|
||||
void handleBleInterface()
|
||||
{
|
||||
if (g_bleRxQueueOverflow)
|
||||
{
|
||||
g_bleRxQueueOverflow = false;
|
||||
Log("BLE RX queue overflow");
|
||||
}
|
||||
|
||||
uint8_t value = 0;
|
||||
while (dequeueBleByte(&value))
|
||||
{
|
||||
setActiveProtocolContext(&g_BLECmd, PROTOCOL_TRANSPORT_BLE);
|
||||
g_BLECmd.readChar(static_cast<char>(value));
|
||||
}
|
||||
}
|
||||
|
||||
bool bleProtocolWrite(const char *message)
|
||||
{
|
||||
if ((message == NULL) || !g_bleClientConnected || (g_pBleTxCharacteristic == NULL))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t *payload = reinterpret_cast<const uint8_t *>(message);
|
||||
size_t remaining = strlen(message);
|
||||
if (remaining == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
while (remaining > 0)
|
||||
{
|
||||
size_t chunkLen = remaining;
|
||||
if (chunkLen > kBleNotifyChunkSize)
|
||||
{
|
||||
chunkLen = kBleNotifyChunkSize;
|
||||
}
|
||||
|
||||
g_pBleTxCharacteristic->setValue(payload, chunkLen);
|
||||
g_pBleTxCharacteristic->notify();
|
||||
|
||||
payload += chunkLen;
|
||||
remaining -= chunkLen;
|
||||
delay(3);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
659
src/Config_Web.cpp
Normal file
659
src/Config_Web.cpp
Normal file
@@ -0,0 +1,659 @@
|
||||
#include "EggDuino.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <DNSServer.h>
|
||||
#include <string.h>
|
||||
|
||||
String g_strConnectionState = "No Wifi connection";
|
||||
|
||||
namespace
|
||||
{
|
||||
const char *kConfigPath = "/config.json";
|
||||
const size_t kConfigJsonCapacity = 4096;
|
||||
const byte kDnsPort = 53;
|
||||
const unsigned long kWifiReconnectIntervalMs = 10000;
|
||||
using ConfigJsonDocument = StaticJsonDocument<kConfigJsonCapacity>;
|
||||
|
||||
WebServer server(80);
|
||||
DNSServer dnsServer;
|
||||
bool configStoreReady = false;
|
||||
bool apModeActive = false;
|
||||
bool staReconnectEnabled = false;
|
||||
bool staConnectionKnown = false;
|
||||
unsigned long lastStaReconnectAttemptMs = 0;
|
||||
ConfigJsonDocument g_configJsonScratch;
|
||||
|
||||
ConfigJsonDocument &configJsonScratch()
|
||||
{
|
||||
// Keep the large config JSON buffer off ESP32 Arduino's 8 KB loopTask stack.
|
||||
g_configJsonScratch.clear();
|
||||
return g_configJsonScratch;
|
||||
}
|
||||
|
||||
void redirectToRoot()
|
||||
{
|
||||
server.sendHeader("Location", String("http://") + WiFi.softAPIP().toString() + "/", true);
|
||||
server.send(302, "text/plain", "");
|
||||
}
|
||||
|
||||
bool isIntType(const ConfigParameter ¶m)
|
||||
{
|
||||
return strcmp(param.type, "int") == 0;
|
||||
}
|
||||
|
||||
int *asIntPtr(ConfigParameter ¶m)
|
||||
{
|
||||
return static_cast<int *>(param.value);
|
||||
}
|
||||
|
||||
String *asStringPtr(ConfigParameter ¶m)
|
||||
{
|
||||
return static_cast<String *>(param.value);
|
||||
}
|
||||
|
||||
ConfigParameter *findParameter(const String &key)
|
||||
{
|
||||
for (size_t i = 0; i < configParameterCount; ++i)
|
||||
{
|
||||
if (key.equals(configParameters[i].key))
|
||||
{
|
||||
return &configParameters[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void applyDefaults()
|
||||
{
|
||||
for (size_t i = 0; i < configParameterCount; ++i)
|
||||
{
|
||||
if (isIntType(configParameters[i]))
|
||||
{
|
||||
*asIntPtr(configParameters[i]) = configParameters[i].defaultValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *fallback = configParameters[i].defaultText == nullptr ? "" : configParameters[i].defaultText;
|
||||
*asStringPtr(configParameters[i]) = String(fallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleRoot()
|
||||
{
|
||||
static const char kPage[] PROGMEM = R"HTML(
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>EggDuino Konfiguration</title>
|
||||
<style>
|
||||
body { font-family: "Segoe UI", sans-serif; margin: 20px; background: #f3f6fb; color: #1a1a1a; }
|
||||
main { max-width: 760px; margin: 0 auto; background: #fff; border-radius: 12px; padding: 20px; box-shadow: 0 8px 24px rgba(0,0,0,0.08); }
|
||||
h1 { margin-top: 0; font-size: 1.35rem; }
|
||||
label { display: block; margin: 14px 0 6px; font-weight: 600; }
|
||||
input[type='number'], input[type='text'], input[type='password'] { width: 100%; padding: 10px; border: 1px solid #c7d2e5; border-radius: 8px; box-sizing: border-box; }
|
||||
button { margin-top: 18px; border: 0; background: #0b5ed7; color: white; padding: 10px 14px; border-radius: 8px; cursor: pointer; }
|
||||
#status { margin-top: 12px; min-height: 1.2em; }
|
||||
#log { margin-top: 20px; border: 1px solid #d6dfef; border-radius: 8px; background: #0f172a; color: #d2e3ff; padding: 10px; height: 220px; overflow-y: auto; white-space: pre-wrap; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.9rem; }
|
||||
#logTitle { margin-top: 24px; margin-bottom: 8px; font-weight: 700; }
|
||||
#downloadPrintLog { display: inline-block; margin-top: 8px; color: #0b5ed7; text-decoration: none; font-weight: 600; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>EggDuino Parameter</h1>
|
||||
<form id="cfgForm"></form>
|
||||
<button id="saveBtn" type="button">Speichern</button>
|
||||
<div id="status"></div>
|
||||
<div id="logTitle">Logs</div>
|
||||
<a id="downloadPrintLog" href="/api/print-log.txt" download="incoming-print-log.txt">Incoming print log herunterladen</a>
|
||||
<div id="log"></div>
|
||||
</main>
|
||||
<script>
|
||||
let lastSeq = 0;
|
||||
|
||||
async function loadConfig() {
|
||||
const resp = await fetch('/api/config', { cache: 'no-store' });
|
||||
if (!resp.ok) throw new Error('Konfiguration konnte nicht geladen werden');
|
||||
return resp.json();
|
||||
}
|
||||
|
||||
function renderForm(config) {
|
||||
const form = document.getElementById('cfgForm');
|
||||
form.innerHTML = '';
|
||||
(config.parameters || []).forEach(p => {
|
||||
const label = document.createElement('label');
|
||||
label.textContent = p.description || p.key;
|
||||
const input = document.createElement('input');
|
||||
const paramType = p.type || 'int';
|
||||
input.type = paramType === 'int' ? 'number' : paramType;
|
||||
if (paramType === 'password') {
|
||||
input.value = '';
|
||||
if (p.hasValue) {
|
||||
input.placeholder = '(gespeichert)';
|
||||
}
|
||||
} else {
|
||||
input.value = p.value ?? '';
|
||||
}
|
||||
input.dataset.key = p.key;
|
||||
input.dataset.ptype = paramType;
|
||||
input.dataset.secret = p.secret ? '1' : '0';
|
||||
input.dataset.touched = '0';
|
||||
input.addEventListener('input', () => {
|
||||
input.dataset.touched = '1';
|
||||
});
|
||||
form.appendChild(label);
|
||||
form.appendChild(input);
|
||||
});
|
||||
}
|
||||
|
||||
async function saveConfig() {
|
||||
const status = document.getElementById('status');
|
||||
const inputs = [...document.querySelectorAll('input[data-key]')];
|
||||
const payload = {
|
||||
parameters: inputs
|
||||
.map(i => {
|
||||
const type = i.dataset.ptype || 'int';
|
||||
const secret = i.dataset.secret === '1';
|
||||
if (type === 'int') {
|
||||
return { key: i.dataset.key, value: Number(i.value) };
|
||||
}
|
||||
if (secret && i.dataset.touched !== '1') {
|
||||
return null;
|
||||
}
|
||||
return { key: i.dataset.key, value: i.value };
|
||||
})
|
||||
.filter(Boolean)
|
||||
};
|
||||
const resp = await fetch('/api/config', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
if (!resp.ok) {
|
||||
const text = await resp.text();
|
||||
throw new Error(text || 'Speichern fehlgeschlagen');
|
||||
}
|
||||
const saved = await resp.json();
|
||||
renderForm(saved);
|
||||
status.textContent = 'Gespeichert';
|
||||
}
|
||||
|
||||
async function pollLogs() {
|
||||
const box = document.getElementById('log');
|
||||
const resp = await fetch('/api/logs?since=' + lastSeq);
|
||||
if (!resp.ok) {
|
||||
return;
|
||||
}
|
||||
const payload = await resp.json();
|
||||
(payload.logs || []).forEach(entry => {
|
||||
box.textContent += entry.text + '\n';
|
||||
});
|
||||
if (typeof payload.lastSeq === 'number') {
|
||||
lastSeq = payload.lastSeq;
|
||||
}
|
||||
box.scrollTop = box.scrollHeight;
|
||||
}
|
||||
|
||||
(async function init() {
|
||||
const status = document.getElementById('status');
|
||||
try {
|
||||
const cfg = await loadConfig();
|
||||
renderForm(cfg);
|
||||
status.textContent = 'Bereit';
|
||||
} catch (e) {
|
||||
status.textContent = e.message;
|
||||
}
|
||||
document.getElementById('saveBtn').addEventListener('click', async () => {
|
||||
try {
|
||||
await saveConfig();
|
||||
} catch (e) {
|
||||
status.textContent = e.message;
|
||||
}
|
||||
});
|
||||
pollLogs();
|
||||
setInterval(pollLogs, 800);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
)HTML";
|
||||
|
||||
server.send(200, "text/html", kPage);
|
||||
}
|
||||
|
||||
void handleGetConfig()
|
||||
{
|
||||
if (!configStoreReady && !initConfigStore())
|
||||
{
|
||||
server.send(500, "text/plain", "Config storage not available");
|
||||
return;
|
||||
}
|
||||
server.sendHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "0");
|
||||
server.send(200, "application/json", buildConfigJson());
|
||||
}
|
||||
|
||||
void handlePostConfig()
|
||||
{
|
||||
if (!configStoreReady && !initConfigStore())
|
||||
{
|
||||
server.send(500, "text/plain", "Config storage not available");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!server.hasArg("plain"))
|
||||
{
|
||||
server.send(400, "text/plain", "Missing JSON body");
|
||||
return;
|
||||
}
|
||||
|
||||
String error;
|
||||
if (!applyConfigJson(server.arg("plain"), error))
|
||||
{
|
||||
Log(String("Config JSON fehlerhaft: ") + error);
|
||||
server.send(400, "text/plain", error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!saveConfigToFile())
|
||||
{
|
||||
Log("Config konnte nicht gespeichert werden");
|
||||
server.send(500, "text/plain", "Could not save config");
|
||||
return;
|
||||
}
|
||||
|
||||
server.send(200, "application/json", buildConfigJson());
|
||||
}
|
||||
|
||||
void handleGetLogs()
|
||||
{
|
||||
uint32_t since = 0;
|
||||
if (server.hasArg("since"))
|
||||
{
|
||||
since = static_cast<uint32_t>(server.arg("since").toInt());
|
||||
}
|
||||
server.send(200, "application/json", buildLogsJson(since));
|
||||
}
|
||||
|
||||
void handleDownloadPrintLog()
|
||||
{
|
||||
server.sendHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
server.sendHeader("Expires", "0");
|
||||
server.sendHeader("Content-Disposition", "attachment; filename=\"incoming-print-log.txt\"");
|
||||
server.send(200, "text/plain; charset=utf-8", buildIncomingPrintLogText());
|
||||
}
|
||||
|
||||
void handleNotFound()
|
||||
{
|
||||
if (apModeActive)
|
||||
{
|
||||
redirectToRoot();
|
||||
return;
|
||||
}
|
||||
if (server.uri().startsWith("/api/"))
|
||||
{
|
||||
server.send(404, "text/plain", "API endpoint not found");
|
||||
return;
|
||||
}
|
||||
handleRoot();
|
||||
}
|
||||
|
||||
void handleWifiReconnect()
|
||||
{
|
||||
if (!staReconnectEnabled || apModeActive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const wl_status_t wifiStatus = WiFi.status();
|
||||
if (wifiStatus == WL_CONNECTED)
|
||||
{
|
||||
if (!staConnectionKnown)
|
||||
{
|
||||
staConnectionKnown = true;
|
||||
Log(String("WLAN verbunden: ") + WiFi.localIP().toString());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (staConnectionKnown)
|
||||
{
|
||||
staConnectionKnown = false;
|
||||
Log("WLAN Verbindung verloren");
|
||||
}
|
||||
|
||||
const unsigned long now = millis();
|
||||
if (now - lastStaReconnectAttemptMs < kWifiReconnectIntervalMs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lastStaReconnectAttemptMs = now;
|
||||
if (!WiFi.reconnect())
|
||||
{
|
||||
WiFi.begin(g_sWifiSsid.c_str(), g_sWifiPassword.c_str());
|
||||
}
|
||||
Log("WLAN Reconnect versucht");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ConfigParameter configParameters[] = {
|
||||
{"int", "penUpPos", &g_iPenUpPos, "Pen Up Position", 40, "", false},
|
||||
{"int", "penDownPos", &g_iPenDownPos, "Pen Down Position", 10, "", false},
|
||||
{"int", "rotStepPin", &g_iRotStepPin, "Rotational Stepper Step Pin", kDefaultRotStepPin, "", false},
|
||||
{"int", "rotDirPin", &g_iRotDirPin, "Rotational Stepper Direction Pin", kDefaultRotDirPin, "", false},
|
||||
{"int", "rotEnablePin", &g_iRotEnablePin, "Rotational Stepper Enable Pin", kDefaultRotEnablePin, "", false},
|
||||
{"int", "penStepPin", &g_iPenStepPin, "Pen Stepper Step Pin", kDefaultPenStepPin, "", false},
|
||||
{"int", "penDirPin", &g_iPenDirPin, "Pen Stepper Direction Pin", kDefaultPenDirPin, "", false},
|
||||
{"int", "penEnablePin", &g_iPenEnablePin, "Pen Stepper Enable Pin", kDefaultPenEnablePin, "", false},
|
||||
{"int", "rotMicrostep", &g_iRotMicrostep, "Rotational Stepper Microsteps", kDefaultRotMicrostep, "", false},
|
||||
{"int", "penMicrostep", &g_iPenMicrostep, "Pen Stepper Microsteps", kDefaultPenMicrostep, "", false},
|
||||
{"int", "iMaxAcclSpeed", &g_iMaxAcclSpeed, "max. Stepper acceleration speed", 10000, "", false},
|
||||
{"int", "servoPin", &g_iServoPin, "Servo Pin", kDefaultServoPin, "", false},
|
||||
{"text", "Name", &g_sHostname, "Name", 0, "EggDuino", false},
|
||||
{"text", "wifiSsid", &g_sWifiSsid, "WLAN SSID", 0, "", false},
|
||||
{"password", "wifiPassword", &g_sWifiPassword, "WLAN Passwort", 0, "", true},
|
||||
};
|
||||
|
||||
const size_t configParameterCount = sizeof(configParameters) / sizeof(configParameters[0]);
|
||||
|
||||
bool initConfigStore()
|
||||
{
|
||||
if (!SPIFFS.begin(true))
|
||||
{
|
||||
configStoreReady = false;
|
||||
return false;
|
||||
}
|
||||
configStoreReady = loadConfigFromFile();
|
||||
return configStoreReady;
|
||||
}
|
||||
|
||||
bool loadConfigFromFile()
|
||||
{
|
||||
applyDefaults();
|
||||
|
||||
File file = SPIFFS.open(kConfigPath, "r");
|
||||
if (!file)
|
||||
{
|
||||
Log("config.json fehlt, defaults werden gespeichert");
|
||||
return saveConfigToFile();
|
||||
}
|
||||
|
||||
ConfigJsonDocument &doc = configJsonScratch();
|
||||
DeserializationError err = deserializeJson(doc, file);
|
||||
file.close();
|
||||
if (err)
|
||||
{
|
||||
Log(String("config.json ist ungueltig (") + err.c_str() + "), defaults werden gespeichert");
|
||||
return saveConfigToFile();
|
||||
}
|
||||
|
||||
JsonArray params = doc["parameters"].as<JsonArray>();
|
||||
for (JsonObject item : params)
|
||||
{
|
||||
const char *key = item["key"];
|
||||
if (key == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ConfigParameter *param = findParameter(String(key));
|
||||
if (param == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (item.containsKey("value"))
|
||||
{
|
||||
if (isIntType(*param))
|
||||
{
|
||||
*asIntPtr(*param) = item["value"].as<int>();
|
||||
}
|
||||
else
|
||||
{
|
||||
*asStringPtr(*param) = item["value"].as<String>();
|
||||
}
|
||||
}
|
||||
if (item.containsKey("description"))
|
||||
{
|
||||
param->description = item["description"].as<String>();
|
||||
}
|
||||
}
|
||||
updateStepCorrectionFactors();
|
||||
Log(String("Config geladen: penUpPos=") + g_iPenUpPos + ", penDownPos=" + g_iPenDownPos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool saveConfigToFile()
|
||||
{
|
||||
File file = SPIFFS.open(kConfigPath, "w");
|
||||
if (!file)
|
||||
{
|
||||
Log("SPIFFS open write failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
ConfigJsonDocument &doc = configJsonScratch();
|
||||
JsonArray params = doc.createNestedArray("parameters");
|
||||
for (size_t i = 0; i < configParameterCount; ++i)
|
||||
{
|
||||
JsonObject item = params.createNestedObject();
|
||||
item["key"] = configParameters[i].key;
|
||||
item["type"] = configParameters[i].type;
|
||||
item["secret"] = configParameters[i].secret;
|
||||
if (isIntType(configParameters[i]))
|
||||
{
|
||||
item["value"] = *asIntPtr(configParameters[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
item["value"] = *asStringPtr(configParameters[i]);
|
||||
}
|
||||
item["description"] = configParameters[i].description;
|
||||
}
|
||||
if (doc.overflowed())
|
||||
{
|
||||
Log("Config JSON Dokument zu klein beim Speichern");
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = serializeJsonPretty(doc, file) > 0;
|
||||
file.flush();
|
||||
file.close();
|
||||
if (!ok)
|
||||
{
|
||||
Log("serializeJsonPretty failed");
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
String buildConfigJson()
|
||||
{
|
||||
ConfigJsonDocument &doc = configJsonScratch();
|
||||
JsonArray params = doc.createNestedArray("parameters");
|
||||
for (size_t i = 0; i < configParameterCount; ++i)
|
||||
{
|
||||
JsonObject item = params.createNestedObject();
|
||||
item["key"] = configParameters[i].key;
|
||||
item["type"] = configParameters[i].type;
|
||||
item["secret"] = configParameters[i].secret;
|
||||
if (isIntType(configParameters[i]))
|
||||
{
|
||||
item["value"] = *asIntPtr(configParameters[i]);
|
||||
}
|
||||
else if (configParameters[i].secret)
|
||||
{
|
||||
item["value"] = "";
|
||||
item["hasValue"] = !asStringPtr(configParameters[i])->isEmpty();
|
||||
}
|
||||
else
|
||||
{
|
||||
item["value"] = *asStringPtr(configParameters[i]);
|
||||
}
|
||||
item["description"] = configParameters[i].description;
|
||||
}
|
||||
if (doc.overflowed())
|
||||
{
|
||||
Log("Config JSON Dokument zu klein beim Lesen");
|
||||
return String("{\"parameters\":[]}");
|
||||
}
|
||||
|
||||
String output;
|
||||
serializeJson(doc, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
bool applyConfigJson(const String &payload, String &errorMessage)
|
||||
{
|
||||
ConfigJsonDocument &doc = configJsonScratch();
|
||||
DeserializationError err = deserializeJson(doc, payload);
|
||||
if (err)
|
||||
{
|
||||
errorMessage = "Invalid JSON payload";
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonArray params = doc["parameters"].as<JsonArray>();
|
||||
if (params.isNull())
|
||||
{
|
||||
errorMessage = "JSON must contain 'parameters' array";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (JsonObject item : params)
|
||||
{
|
||||
const char *key = item["key"];
|
||||
if (key == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ConfigParameter *param = findParameter(String(key));
|
||||
if (param == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!item.containsKey("value"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (isIntType(*param))
|
||||
{
|
||||
*asIntPtr(*param) = item["value"].as<int>();
|
||||
}
|
||||
else
|
||||
{
|
||||
*asStringPtr(*param) = item["value"].as<String>();
|
||||
}
|
||||
if (item.containsKey("description"))
|
||||
{
|
||||
param->description = item["description"].as<String>();
|
||||
}
|
||||
}
|
||||
updateStepCorrectionFactors();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void startWebInterface()
|
||||
{
|
||||
initConfigStore();
|
||||
bool staConnected = false;
|
||||
apModeActive = false;
|
||||
staReconnectEnabled = !g_sWifiSsid.isEmpty();
|
||||
staConnectionKnown = false;
|
||||
lastStaReconnectAttemptMs = millis();
|
||||
dnsServer.stop();
|
||||
char deviceName[32] = {0};
|
||||
buildDeviceName(deviceName, sizeof(deviceName));
|
||||
String hostName = String(deviceName);
|
||||
if (hostName.isEmpty())
|
||||
{
|
||||
hostName = "EggBot_UNKNOWN";
|
||||
}
|
||||
g_sHostname = hostName;
|
||||
|
||||
if (!g_sWifiSsid.isEmpty())
|
||||
{
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.setAutoReconnect(true);
|
||||
WiFi.setHostname(hostName.c_str());
|
||||
WiFi.begin(g_sWifiSsid.c_str(), g_sWifiPassword.c_str());
|
||||
|
||||
const unsigned long connectStart = millis();
|
||||
const unsigned long connectTimeoutMs = 20000;
|
||||
Serial.println(String("Trying to connect to Wifi: ") + g_sWifiSsid.c_str() + g_sWifiPassword.c_str());
|
||||
while (WiFi.status() != WL_CONNECTED && millis() - connectStart < connectTimeoutMs)
|
||||
{
|
||||
delay(250);
|
||||
}
|
||||
staConnected = (WiFi.status() == WL_CONNECTED);
|
||||
staConnectionKnown = staConnected;
|
||||
if (staConnected)
|
||||
{
|
||||
g_strConnectionState = String("Connected: http://") + WiFi.localIP().toString();
|
||||
Serial.println(g_strConnectionState);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("WLAN Verbindung fehlgeschlagen");
|
||||
}
|
||||
|
||||
server.on("/", HTTP_GET, handleRoot);
|
||||
server.on("/api/config", HTTP_GET, handleGetConfig);
|
||||
server.on("/api/config", HTTP_POST, handlePostConfig);
|
||||
server.on("/api/logs", HTTP_GET, handleGetLogs);
|
||||
server.on("/api/print-log.txt", HTTP_GET, handleDownloadPrintLog);
|
||||
server.onNotFound(handleNotFound);
|
||||
server.begin();
|
||||
startWifiProtocolInterface();
|
||||
}
|
||||
if (!staConnected)
|
||||
{
|
||||
WiFi.mode(WIFI_AP);
|
||||
#ifdef ESP32
|
||||
WiFi.softAPsetHostname(hostName.c_str());
|
||||
#endif
|
||||
if (WiFi.softAP(hostName.c_str()))
|
||||
{
|
||||
apModeActive = true;
|
||||
dnsServer.start(kDnsPort, "*", WiFi.softAPIP());
|
||||
g_strConnectionState = String("AP aktiv: http://" + WiFi.softAPIP().toString() + " (" + hostName + ")");
|
||||
Serial.println(g_strConnectionState);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("AP konnte nicht gestartet werden");
|
||||
}
|
||||
}
|
||||
|
||||
server.on("/", HTTP_GET, handleRoot);
|
||||
server.on("/api/config", HTTP_GET, handleGetConfig);
|
||||
server.on("/api/config", HTTP_POST, handlePostConfig);
|
||||
server.on("/api/logs", HTTP_GET, handleGetLogs);
|
||||
server.on("/api/print-log.txt", HTTP_GET, handleDownloadPrintLog);
|
||||
server.on("/generate_204", HTTP_GET, redirectToRoot);
|
||||
server.on("/gen_204", HTTP_GET, redirectToRoot);
|
||||
server.on("/hotspot-detect.html", HTTP_GET, redirectToRoot);
|
||||
server.on("/connecttest.txt", HTTP_GET, redirectToRoot);
|
||||
server.on("/ncsi.txt", HTTP_GET, redirectToRoot);
|
||||
server.on("/fwlink", HTTP_GET, redirectToRoot);
|
||||
server.onNotFound(handleNotFound);
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void handleWebInterface()
|
||||
{
|
||||
handleWifiReconnect();
|
||||
|
||||
if (apModeActive)
|
||||
{
|
||||
dnsServer.processNextRequest();
|
||||
}
|
||||
server.handleClient();
|
||||
}
|
||||
369
src/Functions.cpp
Normal file
369
src/Functions.cpp
Normal file
@@ -0,0 +1,369 @@
|
||||
#include "EggDuino.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
bool g_bPrintSessionActive = false;
|
||||
|
||||
void captureSerialProtocolLine(const char *line)
|
||||
{
|
||||
captureIncomingPrintLine(line, PROTOCOL_TRANSPORT_SERIAL);
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
void captureBleProtocolLine(const char *line)
|
||||
{
|
||||
captureIncomingPrintLine(line, PROTOCOL_TRANSPORT_BLE);
|
||||
}
|
||||
|
||||
void captureWifiProtocolLine(const char *line)
|
||||
{
|
||||
captureIncomingPrintLine(line, PROTOCOL_TRANSPORT_WIFI);
|
||||
}
|
||||
#endif
|
||||
|
||||
void startPrintSessionIfNeeded()
|
||||
{
|
||||
if (!g_bPrintSessionActive)
|
||||
{
|
||||
clearIncomingPrintLog();
|
||||
g_bPrintSessionActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
void stopPrintSession()
|
||||
{
|
||||
g_bPrintSessionActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
void queryPen()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
char state;
|
||||
if (g_iPenState == g_iPenUpPos)
|
||||
state = '1';
|
||||
else
|
||||
state = '0';
|
||||
protocolWrite(String(state) + "\r\n");
|
||||
sendAck();
|
||||
}
|
||||
|
||||
void queryButton()
|
||||
{
|
||||
protocolWrite(String(g_bPrgButtonState) + "\r\n");
|
||||
sendAck();
|
||||
g_bPrgButtonState = 0;
|
||||
}
|
||||
|
||||
void queryLayer()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
protocolWrite(String(g_uiLayer) + "\r\n");
|
||||
sendAck();
|
||||
}
|
||||
|
||||
void setLayer()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
uint32_t value = 0;
|
||||
char *arg1;
|
||||
arg1 = nextCommandArg();
|
||||
if (arg1 != NULL)
|
||||
{
|
||||
value = atoi(arg1);
|
||||
g_uiLayer = value;
|
||||
sendAck();
|
||||
}
|
||||
else
|
||||
sendError();
|
||||
}
|
||||
|
||||
void queryNodeCount()
|
||||
{
|
||||
protocolWrite(String(g_uiNodeCount) + "\r\n");
|
||||
sendAck();
|
||||
}
|
||||
|
||||
void setNodeCount()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
uint32_t value = 0;
|
||||
char *arg1;
|
||||
arg1 = nextCommandArg();
|
||||
if (arg1 != NULL)
|
||||
{
|
||||
value = atoi(arg1);
|
||||
g_uiNodeCount = value;
|
||||
sendAck();
|
||||
}
|
||||
else
|
||||
sendError();
|
||||
}
|
||||
|
||||
void nodeCountIncrement()
|
||||
{
|
||||
g_uiNodeCount++;
|
||||
sendAck();
|
||||
}
|
||||
|
||||
void nodeCountDecrement()
|
||||
{
|
||||
g_uiNodeCount--;
|
||||
sendAck();
|
||||
}
|
||||
|
||||
void stepperMove()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
uint16_t duration = 0; // in ms
|
||||
int penStepsEBB = 0; // Pen
|
||||
int rotStepsEBB = 0; // Rot
|
||||
|
||||
if (!parseSMArgs(&duration, &penStepsEBB, &rotStepsEBB))
|
||||
{
|
||||
sendError();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if ((penStepsEBB == 0) && (rotStepsEBB == 0))
|
||||
{
|
||||
// delay(duration);
|
||||
sendAck();
|
||||
return;
|
||||
}
|
||||
|
||||
startPrintSessionIfNeeded();
|
||||
|
||||
prepareMove(duration, penStepsEBB, rotStepsEBB);
|
||||
moveToDestination();
|
||||
sendAck();
|
||||
}
|
||||
|
||||
void setPen()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
int cmd;
|
||||
char *arg;
|
||||
|
||||
// moveToDestination();
|
||||
|
||||
arg = nextCommandArg();
|
||||
if (arg != NULL)
|
||||
{
|
||||
cmd = atoi(arg);
|
||||
switch (cmd)
|
||||
{
|
||||
case 0:
|
||||
movePenServoTo(g_iPenUpPos);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
movePenServoTo(g_iPenDownPos);
|
||||
break;
|
||||
|
||||
default:
|
||||
sendError();
|
||||
}
|
||||
}
|
||||
char *val;
|
||||
val = nextCommandArg();
|
||||
if (val != NULL)
|
||||
{
|
||||
(void)atoi(val);
|
||||
sendAck();
|
||||
// delay(value);
|
||||
}
|
||||
if (val == NULL && arg != NULL)
|
||||
{
|
||||
sendAck();
|
||||
// delay(500);
|
||||
}
|
||||
// Serial.println("delay");
|
||||
if (val == NULL && arg == NULL)
|
||||
sendError();
|
||||
}
|
||||
|
||||
void togglePen()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
(void)nextCommandArg();
|
||||
|
||||
doTogglePen();
|
||||
sendAck();
|
||||
|
||||
}
|
||||
|
||||
void doTogglePen()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
if (g_iPenState == g_iPenUpPos)
|
||||
{
|
||||
movePenServoTo(g_iPenDownPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
movePenServoTo(g_iPenUpPos);
|
||||
}
|
||||
}
|
||||
|
||||
void enableMotors()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
int cmd;
|
||||
int value;
|
||||
char *arg;
|
||||
char *val;
|
||||
arg = nextCommandArg();
|
||||
if (arg != NULL)
|
||||
cmd = atoi(arg);
|
||||
val = nextCommandArg();
|
||||
if (val != NULL)
|
||||
value = atoi(val);
|
||||
// values parsed
|
||||
if ((arg != NULL) && (val == NULL))
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case 0:
|
||||
stopPrintSession();
|
||||
motorsOff();
|
||||
sendAck();
|
||||
break;
|
||||
case 1:
|
||||
startPrintSessionIfNeeded();
|
||||
motorsOn();
|
||||
sendAck();
|
||||
break;
|
||||
default:
|
||||
sendError();
|
||||
}
|
||||
}
|
||||
// the following implementaion is a little bit cheated, because i did not know, how to implement different values for first and second argument.
|
||||
if ((arg != NULL) && (val != NULL))
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 0:
|
||||
stopPrintSession();
|
||||
motorsOff();
|
||||
sendAck();
|
||||
break;
|
||||
case 1:
|
||||
startPrintSessionIfNeeded();
|
||||
motorsOn();
|
||||
sendAck();
|
||||
break;
|
||||
default:
|
||||
sendError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stepperModeConfigure()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
int cmd;
|
||||
int value;
|
||||
int iTmpPenPos;
|
||||
char *arg;
|
||||
arg = nextCommandArg();
|
||||
if (arg != NULL)
|
||||
cmd = atoi(arg);
|
||||
char *val;
|
||||
val = nextCommandArg();
|
||||
if (val != NULL)
|
||||
value = atoi(val);
|
||||
if ((arg != NULL) && (val != NULL))
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case 4:
|
||||
iTmpPenPos = (int)((float)(value - 6000) / (float)133.3); // transformation from EBB to PWM-Servo
|
||||
if (g_iPenDownPos != iTmpPenPos)
|
||||
{
|
||||
g_iPenDownPos = iTmpPenPos;
|
||||
storePenDownPosInEE();
|
||||
}
|
||||
sendAck();
|
||||
break;
|
||||
case 5:
|
||||
iTmpPenPos = (int)((float)(value - 6000) / (float)133.3); // transformation from EBB to PWM-Servo
|
||||
if (g_iPenUpPos != iTmpPenPos)
|
||||
{
|
||||
g_iPenUpPos = iTmpPenPos;
|
||||
storePenUpPosInEE();
|
||||
}
|
||||
sendAck();
|
||||
break;
|
||||
case 6: // rotMin=value; ignored
|
||||
sendAck();
|
||||
break;
|
||||
case 7: // rotMax=value; ignored
|
||||
sendAck();
|
||||
break;
|
||||
case 11:
|
||||
g_iServoRateUp = value;
|
||||
sendAck();
|
||||
break;
|
||||
case 12:
|
||||
g_iServoRateDown = value;
|
||||
sendAck();
|
||||
break;
|
||||
default:
|
||||
sendError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sendVersion()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
protocolWrite("EBB: " + g_strConnectionState);
|
||||
protocolWrite("\r\n");
|
||||
}
|
||||
|
||||
|
||||
void unrecognized(const char *command)
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
sendError();
|
||||
}
|
||||
|
||||
void ignore()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
sendAck();
|
||||
}
|
||||
|
||||
void makeComInterface()
|
||||
{
|
||||
auto registerCommands = [](SerialCommand &cmd) {
|
||||
cmd.addCommand("v", sendVersion);
|
||||
cmd.addCommand("EM", enableMotors);
|
||||
cmd.addCommand("SC", stepperModeConfigure);
|
||||
cmd.addCommand("SP", setPen);
|
||||
cmd.addCommand("SM", stepperMove);
|
||||
cmd.addCommand("SE", ignore);
|
||||
cmd.addCommand("TP", togglePen);
|
||||
cmd.addCommand("PO", ignore); // Engraver command, not implemented, gives fake answer
|
||||
cmd.addCommand("NI", nodeCountIncrement);
|
||||
cmd.addCommand("ND", nodeCountDecrement);
|
||||
cmd.addCommand("SN", setNodeCount);
|
||||
cmd.addCommand("QN", queryNodeCount);
|
||||
cmd.addCommand("SL", setLayer);
|
||||
cmd.addCommand("QL", queryLayer);
|
||||
cmd.addCommand("QP", queryPen);
|
||||
cmd.addCommand("QB", queryButton); //"PRG" Button,
|
||||
cmd.setDefaultHandler(unrecognized); // Handler for command that isn't matched (says "What?")
|
||||
};
|
||||
|
||||
registerCommands(SCmd);
|
||||
SCmd.setLineHandler(captureSerialProtocolLine);
|
||||
#ifdef ESP32
|
||||
registerCommands(g_BLECmd);
|
||||
g_BLECmd.setLineHandler(captureBleProtocolLine);
|
||||
registerCommands(g_WifiCmd);
|
||||
g_WifiCmd.setLineHandler(captureWifiProtocolLine);
|
||||
#endif
|
||||
}
|
||||
250
src/Helper_Functions.cpp
Normal file
250
src/Helper_Functions.cpp
Normal file
@@ -0,0 +1,250 @@
|
||||
#include "EggDuino.h"
|
||||
#include "motion_planner.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
#ifdef ESP32
|
||||
constexpr char kDeviceNamePrefix[] = "EggBot_";
|
||||
#endif
|
||||
|
||||
int clampServoAngle(int angle)
|
||||
{
|
||||
if (angle < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (angle > 180)
|
||||
{
|
||||
return 180;
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
|
||||
uint_fast16_t servoSpeedFromRate(int rate)
|
||||
{
|
||||
// EBB rate values are implementation-specific. We map them to ServoEasing degrees/second.
|
||||
// Higher rate means faster movement.
|
||||
if (rate <= 0)
|
||||
{
|
||||
return 70;
|
||||
}
|
||||
int speed = 20 + (rate / 2);
|
||||
if (speed < 10)
|
||||
{
|
||||
speed = 10;
|
||||
}
|
||||
if (speed > 360)
|
||||
{
|
||||
speed = 360;
|
||||
}
|
||||
return (uint_fast16_t)speed;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
void buildDeviceName(char *nameBuffer, size_t bufferSize)
|
||||
{
|
||||
if ((nameBuffer == NULL) || (bufferSize == 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Tasmota-style chip ID uses the lower 24 bits of the ESP32 efuse MAC.
|
||||
const uint32_t chipId = static_cast<uint32_t>(ESP.getEfuseMac() & 0xFFFFFFULL);
|
||||
snprintf(nameBuffer, bufferSize, "%s%06X", kDeviceNamePrefix, chipId);
|
||||
}
|
||||
#endif
|
||||
|
||||
void updateStepCorrectionFactors()
|
||||
{
|
||||
if (g_iRotMicrostep <= 0)
|
||||
{
|
||||
g_iRotMicrostep = kDefaultRotMicrostep;
|
||||
}
|
||||
if (g_iPenMicrostep <= 0)
|
||||
{
|
||||
g_iPenMicrostep = kDefaultPenMicrostep;
|
||||
}
|
||||
fROT_STEP_CORRECTION = 16.0f / (float)g_iRotMicrostep;
|
||||
fPEN_STEP_CORRECTION = 16.0f / (float)g_iPenMicrostep;
|
||||
}
|
||||
|
||||
XYMotionPlanner *g_pPlanner = nullptr;
|
||||
|
||||
void initHardware()
|
||||
{
|
||||
if (!initConfigStore())
|
||||
{
|
||||
g_iPenUpPos = 5;
|
||||
g_iPenDownPos = 20;
|
||||
}
|
||||
updateStepCorrectionFactors();
|
||||
g_iPenState = g_iPenDownPos;
|
||||
|
||||
g_stepEngine.init();
|
||||
g_pStepperRotate = g_stepEngine.stepperConnectToPin(g_iRotStepPin);
|
||||
if (g_pStepperRotate)
|
||||
{
|
||||
// rotMotor.setMaxSpeed(2000.0);
|
||||
// rotMotor.setAcceleration(10000.0);
|
||||
|
||||
g_pStepperRotate->setDirectionPin(g_iRotDirPin);
|
||||
g_pStepperRotate->setEnablePin(g_iRotEnablePin);
|
||||
g_pStepperRotate->setAcceleration(g_iMaxAcclSpeed);
|
||||
g_pStepperRotate->setAutoEnable(false);
|
||||
}
|
||||
|
||||
// Stepper pen init
|
||||
g_pStepperPen = g_stepEngine.stepperConnectToPin(g_iPenStepPin);
|
||||
if (g_pStepperPen)
|
||||
{
|
||||
// penMotor.setMaxSpeed(2000.0);
|
||||
// penMotor.setAcceleration(10000.0);
|
||||
g_pStepperPen->setDirectionPin(g_iPenDirPin);
|
||||
g_pStepperPen->setEnablePin(g_iPenEnablePin);
|
||||
g_pStepperPen->setAcceleration(g_iMaxAcclSpeed);
|
||||
g_pStepperPen->setAutoEnable(false);
|
||||
}
|
||||
|
||||
motorsOff();
|
||||
|
||||
g_pPlanner = new XYMotionPlanner(g_pStepperPen, g_pStepperRotate);
|
||||
g_pPlanner->setBaseLimits(4000, g_iMaxAcclSpeed);
|
||||
g_pPlanner->setMinimums(50, 100);
|
||||
g_pPlanner->setCurrentPosition(0, 0);
|
||||
|
||||
g_iPenState = clampServoAngle(g_iPenState);
|
||||
penServo.attach(g_iServoPin, g_iPenState);
|
||||
penServo.setEasingType(EASE_QUADRATIC_IN_OUT);
|
||||
}
|
||||
|
||||
void movePenServoTo(int targetPos)
|
||||
{
|
||||
targetPos = clampServoAngle(targetPos);
|
||||
int currentPos = clampServoAngle(g_iPenState);
|
||||
if (currentPos == targetPos)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint_fast16_t speed = 0;
|
||||
speed = servoSpeedFromRate(g_iServoRateDown);
|
||||
penServo.easeTo(targetPos, speed);
|
||||
|
||||
g_iPenState = targetPos;
|
||||
}
|
||||
|
||||
void storePenUpPosInEE()
|
||||
{
|
||||
saveConfigToFile();
|
||||
}
|
||||
|
||||
void storePenDownPosInEE()
|
||||
{
|
||||
saveConfigToFile();
|
||||
}
|
||||
|
||||
void sendAck()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
protocolWrite("OK\r\n");
|
||||
}
|
||||
|
||||
void sendError()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
protocolWrite("unknown CMD\r\n");
|
||||
}
|
||||
|
||||
void motorsOff()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
g_pStepperPen->disableOutputs();
|
||||
g_pStepperRotate->disableOutputs();
|
||||
g_bMotorsEnabled = 0;
|
||||
}
|
||||
|
||||
void motorsOn()
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
g_pStepperPen->enableOutputs();
|
||||
g_pStepperRotate->enableOutputs();
|
||||
g_bMotorsEnabled = 1;
|
||||
}
|
||||
|
||||
void toggleMotors()
|
||||
|
||||
{
|
||||
Log(__FUNCTION__);
|
||||
if (g_bMotorsEnabled)
|
||||
{
|
||||
motorsOff();
|
||||
}
|
||||
else
|
||||
{
|
||||
motorsOn();
|
||||
}
|
||||
}
|
||||
|
||||
bool parseSMArgs(uint16_t *duration, int *penStepsEBB, int *rotStepsEBB)
|
||||
{
|
||||
char *arg1 = NULL;
|
||||
char *arg2 = NULL;
|
||||
char *arg3 = NULL;
|
||||
arg1 = nextCommandArg();
|
||||
if (arg1 != NULL)
|
||||
{
|
||||
*duration = atoi(arg1);
|
||||
arg2 = nextCommandArg();
|
||||
}
|
||||
if (arg2 != NULL)
|
||||
{
|
||||
*penStepsEBB = atoi(arg2);
|
||||
arg3 = nextCommandArg();
|
||||
}
|
||||
if (arg3 != NULL)
|
||||
{
|
||||
*rotStepsEBB = atoi(arg3);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void prepareMove(uint16_t duration, int penStepsEBB, int rotStepsEBB)
|
||||
{
|
||||
if (!g_bMotorsEnabled)
|
||||
{
|
||||
motorsOn();
|
||||
}
|
||||
|
||||
g_pPlanner->moveBy(penStepsEBB / fPEN_STEP_CORRECTION, rotStepsEBB / fROT_STEP_CORRECTION);
|
||||
}
|
||||
|
||||
void moveOneStep()
|
||||
{
|
||||
Log("moveOneStep");
|
||||
while (g_pStepperPen->isRunning() || g_pStepperRotate->isRunning() || g_pPlanner->isRunning())
|
||||
{
|
||||
g_pPlanner->update();
|
||||
}
|
||||
g_pPlanner->update();
|
||||
Log("done");
|
||||
}
|
||||
|
||||
void moveToDestination()
|
||||
{
|
||||
Log("moveToDestination");
|
||||
while (g_pStepperPen->isRunning() || g_pStepperRotate->isRunning() || g_pPlanner->isRunning())
|
||||
{
|
||||
g_pPlanner->update();
|
||||
}
|
||||
g_pPlanner->update();
|
||||
Log("done");
|
||||
}
|
||||
|
||||
void setprgButtonState()
|
||||
{
|
||||
g_bPrgButtonState = 1;
|
||||
}
|
||||
152
src/Logging.cpp
Normal file
152
src/Logging.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include "EggDuino.h"
|
||||
|
||||
namespace {
|
||||
constexpr size_t kLogCapacity = 80;
|
||||
constexpr size_t kLogLineLength = 160;
|
||||
constexpr size_t kIncomingLogCapacity = 128;
|
||||
constexpr size_t kIncomingLogLineLength = SERIALCOMMAND_BUFFER + 24;
|
||||
|
||||
char g_logLines[kLogCapacity][kLogLineLength];
|
||||
uint32_t g_logSeq[kLogCapacity];
|
||||
size_t g_logWritePos = 0;
|
||||
uint32_t g_nextLogSeq = 1;
|
||||
|
||||
char g_incomingLogLines[kIncomingLogCapacity][kIncomingLogLineLength];
|
||||
uint32_t g_incomingLogSeq[kIncomingLogCapacity];
|
||||
size_t g_incomingLogWritePos = 0;
|
||||
uint32_t g_nextIncomingLogSeq = 1;
|
||||
|
||||
void appendJsonEscaped(String &out, const char *text) {
|
||||
out += "\"";
|
||||
for (size_t i = 0; text[i] != '\0'; ++i) {
|
||||
const char c = text[i];
|
||||
switch (c) {
|
||||
case '\\':
|
||||
out += "\\\\";
|
||||
break;
|
||||
case '"':
|
||||
out += "\\\"";
|
||||
break;
|
||||
case '\n':
|
||||
out += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
out += "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
out += "\\t";
|
||||
break;
|
||||
default:
|
||||
if (static_cast<unsigned char>(c) < 0x20) {
|
||||
out += '?';
|
||||
} else {
|
||||
out += c;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
out += "\"";
|
||||
}
|
||||
|
||||
const char *transportLabel(ProtocolTransport transport) {
|
||||
switch (transport) {
|
||||
case PROTOCOL_TRANSPORT_BLE:
|
||||
return "BLE";
|
||||
case PROTOCOL_TRANSPORT_WIFI:
|
||||
return "WIFI";
|
||||
case PROTOCOL_TRANSPORT_SERIAL:
|
||||
default:
|
||||
return "SERIAL";
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Log(const String &message) {
|
||||
snprintf(
|
||||
g_logLines[g_logWritePos],
|
||||
kLogLineLength,
|
||||
"[%010lu] %s",
|
||||
static_cast<unsigned long>(millis()),
|
||||
message.c_str()
|
||||
);
|
||||
g_logSeq[g_logWritePos] = g_nextLogSeq++;
|
||||
g_logWritePos = (g_logWritePos + 1) % kLogCapacity;
|
||||
}
|
||||
|
||||
void Log(const char *message) {
|
||||
Log(String(message));
|
||||
}
|
||||
|
||||
String buildLogsJson(uint32_t sinceSeq) {
|
||||
String output;
|
||||
output.reserve(2048);
|
||||
output += "{\"logs\":[";
|
||||
uint32_t lastSeq = sinceSeq;
|
||||
bool first = true;
|
||||
|
||||
for (size_t i = 0; i < kLogCapacity; ++i) {
|
||||
const size_t idx = (g_logWritePos + i) % kLogCapacity;
|
||||
const uint32_t seq = g_logSeq[idx];
|
||||
if (seq == 0 || seq <= sinceSeq) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!first) {
|
||||
output += ",";
|
||||
}
|
||||
first = false;
|
||||
output += "{\"seq\":";
|
||||
output += String(seq);
|
||||
output += ",\"text\":";
|
||||
appendJsonEscaped(output, g_logLines[idx]);
|
||||
output += "}";
|
||||
lastSeq = seq;
|
||||
}
|
||||
|
||||
output += "],\"lastSeq\":";
|
||||
output += String(lastSeq);
|
||||
output += "}";
|
||||
return output;
|
||||
}
|
||||
|
||||
void captureIncomingPrintLine(const char *line, ProtocolTransport transport) {
|
||||
if ((line == NULL) || (line[0] == '\0')) {
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(
|
||||
g_incomingLogLines[g_incomingLogWritePos],
|
||||
kIncomingLogLineLength,
|
||||
"[%010lu] %s %s",
|
||||
static_cast<unsigned long>(millis()),
|
||||
transportLabel(transport),
|
||||
line
|
||||
);
|
||||
g_incomingLogSeq[g_incomingLogWritePos] = g_nextIncomingLogSeq++;
|
||||
g_incomingLogWritePos = (g_incomingLogWritePos + 1) % kIncomingLogCapacity;
|
||||
}
|
||||
|
||||
String buildIncomingPrintLogText() {
|
||||
String output;
|
||||
output.reserve(4096);
|
||||
|
||||
for (size_t i = 0; i < kIncomingLogCapacity; ++i) {
|
||||
const size_t idx = (g_incomingLogWritePos + i) % kIncomingLogCapacity;
|
||||
if (g_incomingLogSeq[idx] == 0) {
|
||||
continue;
|
||||
}
|
||||
output += g_incomingLogLines[idx];
|
||||
output += "\n";
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void clearIncomingPrintLog() {
|
||||
for (size_t i = 0; i < kIncomingLogCapacity; ++i) {
|
||||
g_incomingLogSeq[i] = 0;
|
||||
g_incomingLogLines[i][0] = '\0';
|
||||
}
|
||||
g_incomingLogWritePos = 0;
|
||||
g_nextIncomingLogSeq = 1;
|
||||
}
|
||||
156
src/WiFi_Protocol.cpp
Normal file
156
src/WiFi_Protocol.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#include "EggDuino.h"
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WebSocketsServer.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr uint16_t kWifiProtocolPort = 1337;
|
||||
constexpr uint8_t kInvalidWifiClientId = 0xFF;
|
||||
constexpr size_t kWifiRxQueueSize = 1024;
|
||||
|
||||
struct WifiRxByte
|
||||
{
|
||||
uint8_t value;
|
||||
};
|
||||
|
||||
WebSocketsServer g_wifiProtocolSocket(kWifiProtocolPort);
|
||||
WifiRxByte g_wifiRxQueue[kWifiRxQueueSize];
|
||||
size_t g_wifiRxHead = 0;
|
||||
size_t g_wifiRxTail = 0;
|
||||
bool g_wifiRxOverflow = false;
|
||||
bool g_wifiProtocolStarted = false;
|
||||
uint8_t g_wifiProtocolClientId = kInvalidWifiClientId;
|
||||
|
||||
bool queueWifiByte(uint8_t value)
|
||||
{
|
||||
const size_t nextHead = (g_wifiRxHead + 1) % kWifiRxQueueSize;
|
||||
if (nextHead == g_wifiRxTail)
|
||||
{
|
||||
g_wifiRxOverflow = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
g_wifiRxQueue[g_wifiRxHead].value = value;
|
||||
g_wifiRxHead = nextHead;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dequeueWifiByte(WifiRxByte *byte)
|
||||
{
|
||||
if ((byte == NULL) || (g_wifiRxHead == g_wifiRxTail))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*byte = g_wifiRxQueue[g_wifiRxTail];
|
||||
g_wifiRxTail = (g_wifiRxTail + 1) % kWifiRxQueueSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
void handleWifiSocketEvent(uint8_t clientId, WStype_t type, uint8_t *payload, size_t length)
|
||||
{
|
||||
(void)payload;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case WStype_CONNECTED:
|
||||
if (g_wifiProtocolClientId == kInvalidWifiClientId)
|
||||
{
|
||||
g_wifiProtocolClientId = clientId;
|
||||
}
|
||||
Log(String("WiFi protocol client connected: ") + clientId);
|
||||
break;
|
||||
|
||||
case WStype_DISCONNECTED:
|
||||
if (g_wifiProtocolClientId == clientId)
|
||||
{
|
||||
g_wifiProtocolClientId = kInvalidWifiClientId;
|
||||
}
|
||||
Log(String("WiFi protocol client disconnected: ") + clientId);
|
||||
break;
|
||||
|
||||
case WStype_TEXT:
|
||||
case WStype_BIN:
|
||||
if (g_wifiProtocolClientId == kInvalidWifiClientId)
|
||||
{
|
||||
g_wifiProtocolClientId = clientId;
|
||||
}
|
||||
if (clientId != g_wifiProtocolClientId)
|
||||
{
|
||||
break;
|
||||
}
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
{
|
||||
queueWifiByte(payload[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void startWifiProtocolInterface()
|
||||
{
|
||||
if (g_wifiProtocolStarted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_wifiProtocolSocket.begin();
|
||||
g_wifiProtocolSocket.onEvent(handleWifiSocketEvent);
|
||||
g_wifiProtocolStarted = true;
|
||||
|
||||
Log(String("WiFi EggBot protocol ws://") + WiFi.localIP().toString() + ":" + kWifiProtocolPort);
|
||||
}
|
||||
|
||||
void handleWifiProtocolInterface()
|
||||
{
|
||||
if (!g_wifiProtocolStarted)
|
||||
{
|
||||
if (WiFi.status() == WL_CONNECTED)
|
||||
{
|
||||
startWifiProtocolInterface();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
g_wifiProtocolSocket.loop();
|
||||
|
||||
if (g_wifiRxOverflow)
|
||||
{
|
||||
g_wifiRxOverflow = false;
|
||||
Log("WiFi protocol RX queue overflow");
|
||||
}
|
||||
|
||||
WifiRxByte byte = {0};
|
||||
while (dequeueWifiByte(&byte))
|
||||
{
|
||||
setActiveProtocolContext(&g_WifiCmd, PROTOCOL_TRANSPORT_WIFI);
|
||||
g_WifiCmd.readChar(static_cast<char>(byte.value));
|
||||
}
|
||||
}
|
||||
|
||||
bool wifiProtocolWrite(const char *message)
|
||||
{
|
||||
if (!g_wifiProtocolStarted || (message == NULL))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((g_wifiProtocolClientId == kInvalidWifiClientId) || !g_wifiProtocolSocket.clientIsConnected(g_wifiProtocolClientId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return g_wifiProtocolSocket.sendTXT(g_wifiProtocolClientId, message);
|
||||
}
|
||||
|
||||
#endif
|
||||
13
src/esp_timer_compat.c
Normal file
13
src/esp_timer_compat.c
Normal file
@@ -0,0 +1,13 @@
|
||||
#if defined(EGGDUINO_WINDOWS_BUILD_FIXES)
|
||||
#include <ArduinoEsp32Compat.h>
|
||||
|
||||
#if defined(ESP32) && EGGDUINO_LEGACY_ARDUINO_ESP32
|
||||
#include <esp_timer.h>
|
||||
|
||||
int esp_timer_is_active(esp_timer_handle_t timer)
|
||||
{
|
||||
(void)timer;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
225
src/main.cpp
Normal file
225
src/main.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
/* Eggduino-Firmware by Joachim Cerny, 2014
|
||||
|
||||
Thanks for the nice libs ACCELSTEPPER and SERIALCOMMAND, which made this project much easier.
|
||||
Thanks to the Eggbot-Team for such a funny and enjoable concept!
|
||||
Thanks to my wife and my daughter for their patience. :-)
|
||||
|
||||
*/
|
||||
|
||||
// implemented Eggbot-Protocol-Version v13
|
||||
// EBB-Command-Reference, I sourced from: http://www.schmalzhaus.com/EBB/EBBCommands.html
|
||||
// no homing sequence, switch-on position of pen will be taken as reference point.
|
||||
// No collision-detection!!
|
||||
// Supported Servos: I do not know, I use Arduino Servo Lib with TG9e- standard servo.
|
||||
// Note: Maximum-Speed in Inkscape is 1000 Steps/s. You could enter more, but then Pythonscript sends nonsense.
|
||||
// EBB-Coordinates are coming in for 16th-Microstepmode. The Coordinate-Transforms are done in weired integer-math. Be careful, when you diecide to modify settings.
|
||||
|
||||
/* TODOs:
|
||||
1 collision control via penMin/penMax
|
||||
2 implement homing sequence via microswitch or optical device
|
||||
*/
|
||||
|
||||
#include "EggDuino.h"
|
||||
#include <ServoEasing.hpp>
|
||||
|
||||
// EXTRAFEATURES - UNCOMMENT TO USE THEM -------------------------------------------------------------------
|
||||
|
||||
// #define prgButton 2 // PRG button
|
||||
// #define penToggleButton 12 // pen up/down button
|
||||
// #define motorsButton 4 // motors enable button
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
|
||||
FastAccelStepperEngine g_stepEngine = FastAccelStepperEngine();
|
||||
FastAccelStepper *g_pStepperRotate = NULL;
|
||||
FastAccelStepper *g_pStepperPen = NULL;
|
||||
|
||||
// make Objects
|
||||
ServoEasing penServo;
|
||||
SerialCommand SCmd;
|
||||
#ifdef ESP32
|
||||
SerialCommand g_BLECmd;
|
||||
SerialCommand g_WifiCmd;
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
SerialCommand *g_pActiveParser = &SCmd;
|
||||
ProtocolTransport g_activeTransport = PROTOCOL_TRANSPORT_SERIAL;
|
||||
}
|
||||
|
||||
// create Buttons
|
||||
#ifdef prgButton
|
||||
Button prgButtonToggle(prgButton, setprgButtonState);
|
||||
#endif
|
||||
#ifdef penToggleButton
|
||||
Button penToggle(penToggleButton, doTogglePen);
|
||||
#endif
|
||||
#ifdef motorsButton
|
||||
Button motorsToggle(motorsButton, toggleMotors);
|
||||
#endif
|
||||
|
||||
// Variables... be careful, by messing around here, everything has a reason and crossrelations...
|
||||
int g_iPenUpPos = 5; // can be overwritten from EBB-Command SC
|
||||
int g_iPenDownPos = 20; // can be overwritten from EBB-Command SC
|
||||
int g_iServoRateUp = 0; // from EBB-Protocol, mapped to ServoEasing speed (up movement)
|
||||
int g_iServoRateDown = 0; // from EBB-Protocol, mapped to ServoEasing speed (down movement)
|
||||
long g_iRotStepError = 0;
|
||||
long g_iPenStepError = 0;
|
||||
int g_iPenState = g_iPenUpPos;
|
||||
uint32_t g_uiNodeCount = 0;
|
||||
unsigned int g_uiLayer = 0;
|
||||
boolean g_bPrgButtonState = 0;
|
||||
boolean g_bMotorsEnabled = 0;
|
||||
int g_iRotDirPin = kDefaultRotDirPin;
|
||||
int g_iRotEnablePin = kDefaultRotEnablePin;
|
||||
int g_iRotStepPin = kDefaultRotStepPin;
|
||||
int g_iPenStepPin = kDefaultPenStepPin;
|
||||
int g_iPenDirPin = kDefaultPenDirPin;
|
||||
int g_iPenEnablePin = kDefaultPenEnablePin;
|
||||
int g_iServoPin = kDefaultServoPin;
|
||||
int g_iRotMicrostep = kDefaultRotMicrostep;
|
||||
int g_iPenMicrostep = kDefaultPenMicrostep;
|
||||
float fROT_STEP_CORRECTION = 16.0 / kDefaultRotMicrostep; // devide EBB-Coordinates by this factor to get EGGduino-Steps
|
||||
float fPEN_STEP_CORRECTION = 16.0 / kDefaultPenMicrostep; // devide EBB-Coordinates by this factor to get EGGduino-Steps
|
||||
int g_iMaxAcclSpeed = 10000;
|
||||
String g_sHostname = "EggDuino";
|
||||
String g_sWifiSsid = "";
|
||||
String g_sWifiPassword = "";
|
||||
|
||||
void setActiveProtocolContext(SerialCommand *parser, ProtocolTransport transport)
|
||||
{
|
||||
if (parser != NULL)
|
||||
{
|
||||
g_pActiveParser = parser;
|
||||
}
|
||||
g_activeTransport = transport;
|
||||
}
|
||||
|
||||
char *nextCommandArg()
|
||||
{
|
||||
return g_pActiveParser->next();
|
||||
}
|
||||
|
||||
void protocolWrite(const char *message)
|
||||
{
|
||||
if (message == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
if (g_activeTransport == PROTOCOL_TRANSPORT_BLE)
|
||||
{
|
||||
if (bleProtocolWrite(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (g_activeTransport == PROTOCOL_TRANSPORT_WIFI)
|
||||
{
|
||||
if (wifiProtocolWrite(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Serial.print(message);
|
||||
}
|
||||
|
||||
void protocolWrite(const String &message)
|
||||
{
|
||||
protocolWrite(message.c_str());
|
||||
}
|
||||
|
||||
// Stepper Test
|
||||
#ifdef TEST
|
||||
// #define dirPinStepper 16
|
||||
// #define enablePinStepper 12
|
||||
// #define stepPinStepper 26
|
||||
#endif
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting...");
|
||||
startWebInterface();
|
||||
makeComInterface();
|
||||
initHardware();
|
||||
startBleInterface();
|
||||
}
|
||||
|
||||
uint8_t g_uiState = 0;
|
||||
unsigned long g_uiLastTim = millis();
|
||||
|
||||
void loop()
|
||||
{
|
||||
#ifdef TEST
|
||||
|
||||
unsigned long uiNow = millis();
|
||||
motorsOn();
|
||||
|
||||
if (uiNow - g_uiLastTim > 5000)
|
||||
{
|
||||
g_uiLastTim = uiNow;
|
||||
switch (g_uiState)
|
||||
{
|
||||
case 0:
|
||||
Log(String(g_uiState));
|
||||
g_pStepperRotate->setSpeedInUs(10); // the parameter is us/step !!!
|
||||
g_pStepperRotate->setAcceleration(10000);
|
||||
g_pStepperRotate->move(1000);
|
||||
g_uiState++;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
Log(String(g_uiState));
|
||||
g_pStepperRotate->setSpeedInUs(10); // the parameter is us/step !!!
|
||||
g_pStepperRotate->setAcceleration(10000);
|
||||
g_pStepperRotate->move(-1000);
|
||||
g_uiState++;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
Log(String(g_uiState));
|
||||
g_pStepperPen->setSpeedInUs(10); // the parameter is us/step !!!
|
||||
g_pStepperPen->setAcceleration(10000);
|
||||
g_pStepperPen->move(1000);
|
||||
g_uiState++;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
Log(String(g_uiState));
|
||||
g_pStepperPen->setSpeedInUs(10); // the parameter is us/step !!!
|
||||
g_pStepperPen->setAcceleration(10000);
|
||||
g_pStepperPen->move(-1000);
|
||||
g_uiState = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Log("Alive");
|
||||
}
|
||||
#else
|
||||
// moveOneStep();
|
||||
setActiveProtocolContext(&SCmd, PROTOCOL_TRANSPORT_SERIAL);
|
||||
SCmd.readSerial();
|
||||
handleBleInterface();
|
||||
handleWebInterface();
|
||||
handleWifiProtocolInterface();
|
||||
#endif
|
||||
|
||||
#ifdef penToggleButton
|
||||
penToggle.check();
|
||||
#endif
|
||||
|
||||
#ifdef motorsButton
|
||||
motorsToggle.check();
|
||||
#endif
|
||||
|
||||
#ifdef prgButton
|
||||
prgButtonToggle.check();
|
||||
#endif
|
||||
}
|
||||
141
src/motion_planner.cpp
Normal file
141
src/motion_planner.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "motion_planner.h"
|
||||
|
||||
XYMotionPlanner::XYMotionPlanner(FastAccelStepper* stepperX, FastAccelStepper* stepperY)
|
||||
: m_x(stepperX), m_y(stepperY) {}
|
||||
|
||||
void XYMotionPlanner::setBaseLimits(uint32_t maxSpeedHz, uint32_t maxAccelStepsPerS2) {
|
||||
m_baseSpeedHz = max<uint32_t>(1, maxSpeedHz);
|
||||
m_baseAccel = max<uint32_t>(1, maxAccelStepsPerS2);
|
||||
}
|
||||
|
||||
void XYMotionPlanner::setMinimums(uint32_t minSpeedHz, uint32_t minAccelStepsPerS2) {
|
||||
m_minSpeedHz = minSpeedHz;
|
||||
m_minAccel = minAccelStepsPerS2;
|
||||
}
|
||||
|
||||
void XYMotionPlanner::setCurrentPosition(int32_t x, int32_t y) {
|
||||
m_currentX = x;
|
||||
m_currentY = y;
|
||||
m_targetX = x;
|
||||
m_targetY = y;
|
||||
if (m_x) {
|
||||
m_x->setCurrentPosition(x);
|
||||
}
|
||||
if (m_y) {
|
||||
m_y->setCurrentPosition(y);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t XYMotionPlanner::currentX() const { return m_currentX; }
|
||||
|
||||
int32_t XYMotionPlanner::currentY() const { return m_currentY; }
|
||||
|
||||
bool XYMotionPlanner::isRunning() const {
|
||||
bool rx = m_x ? m_x->isRunning() : false;
|
||||
bool ry = m_y ? m_y->isRunning() : false;
|
||||
return rx || ry;
|
||||
}
|
||||
|
||||
void XYMotionPlanner::waitUntilFinished() {
|
||||
while (isRunning()) {
|
||||
delay(1);
|
||||
}
|
||||
m_currentX = m_targetX;
|
||||
m_currentY = m_targetY;
|
||||
}
|
||||
|
||||
bool XYMotionPlanner::moveTo(int32_t targetX, int32_t targetY) {
|
||||
if ((m_x == nullptr) || (m_y == nullptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Keep planner state in sync when the previous move already ended,
|
||||
// but update() has not been called by the caller yet.
|
||||
update();
|
||||
|
||||
m_targetX = targetX;
|
||||
m_targetY = targetY;
|
||||
|
||||
int32_t dxSigned = m_targetX - m_currentX;
|
||||
int32_t dySigned = m_targetY - m_currentY;
|
||||
|
||||
uint32_t dx = (dxSigned >= 0) ? dxSigned : -dxSigned;
|
||||
uint32_t dy = (dySigned >= 0) ? dySigned : -dySigned;
|
||||
|
||||
if ((dx == 0) && (dy == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t dmax = max(dx, dy);
|
||||
|
||||
// Skalierungsfaktoren relativ zur dominanten Achse
|
||||
float sx = (float)dx / (float)dmax;
|
||||
float sy = (float)dy / (float)dmax;
|
||||
|
||||
// Dominante Achse bekommt die Basiswerte
|
||||
uint32_t speedX = scaledValue(m_baseSpeedHz, sx, dx);
|
||||
uint32_t speedY = scaledValue(m_baseSpeedHz, sy, dy);
|
||||
uint32_t accelX = scaledValue(m_baseAccel, sx, dx);
|
||||
uint32_t accelY = scaledValue(m_baseAccel, sy, dy);
|
||||
|
||||
// Kurze Achsen mit Bewegung sollen nicht auf 0 fallen
|
||||
if (dx > 0) {
|
||||
speedX = max(speedX, m_minSpeedHz);
|
||||
accelX = max(accelX, m_minAccel);
|
||||
}
|
||||
if (dy > 0) {
|
||||
speedY = max(speedY, m_minSpeedHz);
|
||||
accelY = max(accelY, m_minAccel);
|
||||
}
|
||||
|
||||
// FastAccelStepper: speed in steps/s, accel in steps/s²
|
||||
if (dx > 0) {
|
||||
m_x->setSpeedInHz(speedX);
|
||||
m_x->setAcceleration(accelX);
|
||||
}
|
||||
if (dy > 0) {
|
||||
m_y->setSpeedInHz(speedY);
|
||||
m_y->setAcceleration(accelY);
|
||||
}
|
||||
|
||||
// Bewegungen anstoßen
|
||||
// Reihenfolge ist hier meist ausreichend.
|
||||
// Für "noch synchroner" müsste man tiefer in die Queue-API gehen.
|
||||
if (dx > 0) {
|
||||
m_x->moveTo(m_targetX);
|
||||
}
|
||||
if (dy > 0) {
|
||||
m_y->moveTo(m_targetY);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XYMotionPlanner::moveBy(int32_t deltaX, int32_t deltaY) {
|
||||
update();
|
||||
|
||||
// Chain relative moves reliably:
|
||||
// - if running, append relative to the current planned target
|
||||
// - if idle, use the current position
|
||||
const int32_t baseX = isRunning() ? m_targetX : m_currentX;
|
||||
const int32_t baseY = isRunning() ? m_targetY : m_currentY;
|
||||
return moveTo(baseX + deltaX, baseY + deltaY);
|
||||
}
|
||||
|
||||
void XYMotionPlanner::update() {
|
||||
if (!isRunning()) {
|
||||
m_currentX = m_targetX;
|
||||
m_currentY = m_targetY;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t XYMotionPlanner::scaledValue(uint32_t base, float scale, uint32_t distance) {
|
||||
if (distance == 0) {
|
||||
return 0;
|
||||
}
|
||||
float v = (float)base * scale;
|
||||
if (v < 1.0f) {
|
||||
v = 1.0f;
|
||||
}
|
||||
return (uint32_t)(v + 0.5f);
|
||||
}
|
||||
669
svg/chicken.svg
Normal file
669
svg/chicken.svg
Normal file
@@ -0,0 +1,669 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="svg6435"
|
||||
version="1.1"
|
||||
viewBox="0 0 3200 1024"
|
||||
sodipodi:docname="chicken.svg"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview287"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.30819089"
|
||||
inkscape:cx="1489.3367"
|
||||
inkscape:cy="340.69794"
|
||||
inkscape:window-width="3454"
|
||||
inkscape:window-height="2088"
|
||||
inkscape:window-x="386"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg6435" />
|
||||
<!-- Generator: Adobe Illustrator 30.2.1, SVG Export Plug-In . SVG Version: 2.1.1 Build 1) -->
|
||||
<defs
|
||||
id="defs4">
|
||||
<style
|
||||
id="style2">
|
||||
.st0 {
|
||||
stroke-miterlimit: 4;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.st0, .st1 {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g
|
||||
id="g34">
|
||||
<path
|
||||
class="st1"
|
||||
d="M624.26,266.87s183.01,17.85,435.95,214.26c0,0-116.06,324.36-369,226.16-252.94-98.2-66.96-440.42-66.96-440.42h.01Z"
|
||||
id="path6" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M645.19,260.45s13.96-95.11,53.22-89.87c0,0,15.28-.66,12.06,22.14,0,0-.72,19.74,12.37,6.65,13.09-13.09,20.94-14.83,30.54-10.47s0,19.2,0,19.2c0,0-3.49,16.37,11.34,7.31s25.1-7.39,30.54,0,9.6,18.86-14.83,35.44c-24.43,16.58-44.39,27.05-59.28,35.77"
|
||||
id="path8" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M569.95,333.83s-49.41,44.04-39.21,76.81c10.21,32.76,42.43-24.72,46.19-69.29"
|
||||
id="path10" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M682.48,460.32s-46.3,168.45,147.76,151.7c0,0,93.58-13.79,129.05-80.78"
|
||||
id="path12" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M677.65,708.56l-98.5,61.08-34.58-43.82s-9.71-8.07-15.19-2.17"
|
||||
id="path14" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M591.33,799.49s6.32-7.16,4.21-11.8-16.4-18.06-16.4-18.06"
|
||||
id="path16" />
|
||||
<path
|
||||
id="line18"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 801.54999,732.08002 45.16998,92.90997" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M781.49,850.33s1.28-13.73,9.09-16.89,90.16-8.45,90.16-8.45c0,0,6.33,0,10.56,12.67"
|
||||
id="path20" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M1043.32,451.24s16.9-114.19,49.62-130.06c21.03-10.2,21.12,14.98,8.45,43.49-12.67,28.51-51.73,95.65-51.73,95.65,0,0,90.8-138.33,116.14-126.49s-94.07,120.29-101.51,131.59c0,0,117.58-116.04,158.52-116.58,24.74-.33,32.23,30.32,6.33,48.57-22.1,15.58-153.09,76.02-153.09,76.02,0,0,141.07-66.3,175.61-53.12,23.01,8.78,19.71,44.67-6.68,53.12s-165.76,18.43-165.76,18.43c0,0,103.47.58,118.25,14.3,14.78,13.73,10.56,46.45-13.73,47.51-24.28,1.06-116.14-49.62-116.14-49.62"
|
||||
id="path22" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M763.11,527.4s15.52,90.93,67.13,84.62"
|
||||
id="path24" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M792.42,531.27s36.67,84.64,85.31,66.26"
|
||||
id="path26" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M532.03,278.8s31.51,38.04,71.49,29.73"
|
||||
id="path28" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M645.19,318.87s27.03-22.09,32.46,10.61"
|
||||
id="path30" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M615.96,266.87h-90.35s21.51,47.2,61.1,66.96"
|
||||
id="path32" />
|
||||
</g>
|
||||
<g
|
||||
id="g58">
|
||||
<path
|
||||
id="circle36"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1863.5199,697.33002 a 111.58,111.58 0 0 1 -111.58,111.58 111.58,111.58 0 0 1 -111.58,-111.58 111.58,111.58 0 0 1 111.58,-111.58 111.58,111.58 0 0 1 111.58,111.58 z" />
|
||||
<path
|
||||
id="polyline38"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1659.98,632.74 -54.71,0.96 35.09,45.53" />
|
||||
<path
|
||||
id="polyline40"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1718.47,803.8 -23.22,9.47 3.75,25.75" />
|
||||
<path
|
||||
id="line42"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1769.73,807.41998 10.04,34.90003" />
|
||||
<path
|
||||
id="line44"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1740.55,842.32001 h 58.37" />
|
||||
<path
|
||||
id="line46"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1674.27,842.32001 44.2,-7.75" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M1742.55,697.33s-9.11,70.2,92.1,71.13c0,0-29.54-32.25-35.73-55.64"
|
||||
id="path48" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M1863.53,687.29s49.55-50.45,67.79-40.28c18.24,10.17,10.95,24.77,4.56,26.59-6.39,1.82-72.35,23.72-72.35,23.72,0,0,63.23-17.34,68.7-3.66,5.47,13.68-5.47,19.15-10.95,19.15s-57.75-6.38-57.75-6.38"
|
||||
id="path50" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M1695.25,633.7s7.26,34.59,31.8,0"
|
||||
id="path52" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M1605.27,633.7s20.05,17.21,40.02,19.49"
|
||||
id="path54" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M1751.94,585.75s25.63-71.09,36.55-59.37c10.92,11.71-25.88,59.37-25.88,59.37,0,0,40.14-56.04,46-39.31,5.86,16.73-33.86,39.31-33.86,39.31,0,0,41.39-25.09,38.04-12.55-3.35,12.55-24.3,18.67-24.3,18.67"
|
||||
id="path56" />
|
||||
</g>
|
||||
<g
|
||||
id="g94">
|
||||
<g
|
||||
id="g78">
|
||||
<path
|
||||
id="circle60"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1483.55,697.33002 a 111.58,111.58 0 0 1 -111.58,111.58 111.58,111.58 0 0 1 -111.58,-111.58 111.58,111.58 0 0 1 111.58,-111.58 111.58,111.58 0 0 1 111.58,111.58 z" />
|
||||
<path
|
||||
id="polyline62"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1280.01,632.74 -49.21,-10.23 29.91,34.51 -41.33,16.77 42.43,10.07" />
|
||||
<path
|
||||
id="circle64"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1350.97,647.01001 a 13.31,13.31 0 0 1 -13.31,13.31 13.31,13.31 0 0 1 -13.31,-13.31 13.31,13.31 0 0 1 13.31,-13.31 13.31,13.31 0 0 1 13.31,13.31 z" />
|
||||
<path
|
||||
id="line66"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1319.64,795.15002 -54.3299,24.52997" />
|
||||
<path
|
||||
id="line68"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1430.75,792.17999 23.9301,50.14002" />
|
||||
<path
|
||||
id="line70"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1415.46,842.32001 h 58.37" />
|
||||
<path
|
||||
id="line72"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1244.87,799.65997 33.89,31.56" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M1362.57,697.33s-9.11,70.2,92.1,71.13"
|
||||
id="path74" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M1483.56,687.29s49.55-50.45,67.79-40.28c18.24,10.17,10.95,24.77,4.56,26.59s-72.35,23.72-72.35,23.72c0,0,63.23-17.34,68.7-3.66,5.47,13.68-5.47,19.15-10.95,19.15s-57.75-6.38-57.75-6.38"
|
||||
id="path76" />
|
||||
</g>
|
||||
<path
|
||||
class="st1"
|
||||
d="M1386.86,578.9s10.66-23.41,28.62-36.54c17.96-13.12,46.37-9.86,62.11,17.06,15.73,26.93-13.35,67.99-17.05,63.09-29.77-40.11-75.48-40.11-75.48-40.11,0,0-64.35-25.28-73.51-31.51-9.17-6.23-3.22-17.72,8.97-10.92s66.36,38.94,66.36,38.94h0Z"
|
||||
id="path80" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M1451.85,537.16s5.76-7.64,10.72-4.5c4.96,3.14,4.24,7.24,2.37,11.83"
|
||||
id="path82" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M1450.58,539.98s-25.06,10.19-35.77,44.64"
|
||||
id="path84" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M1464.68,547.54s5.23,14.62-18.28,54.56"
|
||||
id="path86" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M1451.85,531.91s-17.29-33.55-24.61-35.38c-4.38-1.1-6.06,2.83-4.03,7.26,2.02,4.44,28.66,28.12,28.66,28.12h-.01Z"
|
||||
id="path88" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M1470.87,544.49s20.63,14.27,30.57,17.04c3.75,1.05,10.21-.94,6.58-8.14-3.63-7.2-37.15-8.9-37.15-8.9h0Z"
|
||||
id="path90" />
|
||||
<path
|
||||
id="circle92"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1345.96,647.12 a 10.25,10.25 0 0 1 -10.25,10.25 10.25,10.25 0 0 1 -10.25,-10.25 10.25,10.25 0 0 1 10.25,-10.25 10.25,10.25 0 0 1 10.25,10.25 z" />
|
||||
</g>
|
||||
<g
|
||||
id="g126">
|
||||
<path
|
||||
class="st1"
|
||||
d="M2234.44,769.7c-20.47,23.99-50.92,39.2-84.92,39.2-61.63,0-111.58-49.95-111.58-111.58s49.95-111.58,111.58-111.58,111.58,49.95,111.58,111.58c0,22.22-6.5,42.93-17.69,60.31"
|
||||
id="path96" />
|
||||
<path
|
||||
id="polyline98"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2057.55,632.74 -59.6,17.29 40.95,32.51" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2155.04,708.52s-3.14,59.01,98.1,59.92c0,0-27.27-23.41-36.91-39.26"
|
||||
id="path100" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2261.08,687.29s49.55-50.45,67.79-40.28,10.95,24.77,4.56,26.59-72.35,23.72-72.35,23.72c0,0,63.23-17.34,68.7-3.66,5.47,13.68-5.47,19.15-10.95,19.15s-57.75-6.38-57.75-6.38"
|
||||
id="path102" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2109.83,650.03c.23-.98.36-1.98.36-3.02,0-7.34-5.96-13.31-13.31-13.31s-13.31,5.96-13.31,13.31c0,1.05.13,2.05.36,3.02"
|
||||
id="path104" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2150.08,582.52l23.6-50.29,3.44,41.32,37.73-44.83-8.79,51.04,26.18-17.64-17.38,40.36s-32.82-22.05-64.78-19.97h0Z"
|
||||
id="path106" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M1997.95,650.03s28.16,10.15,48.38,4.77"
|
||||
id="path108" />
|
||||
<path
|
||||
id="circle110"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2127.6899,686.03998 a 17.5,17.5 0 0 1 -17.5,17.5 17.5,17.5 0 0 1 -17.5,-17.5 17.5,17.5 0 0 1 17.5,-17.5 17.5,17.5 0 0 1 17.5,17.5 z" />
|
||||
<path
|
||||
id="line112"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2057.55,762.76001 -59.6,13.77997" />
|
||||
<path
|
||||
id="line114"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="M 1990.23,746.22998 2001.9301,788.94" />
|
||||
<path
|
||||
id="polyline116"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2120.85,805.19 -24.5,18.66 7.52,23.63" />
|
||||
<path
|
||||
id="line118"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2079.76,847.47998 h 43.3699" />
|
||||
<path
|
||||
id="line120"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2107.6299,639.15997 6.2002,-4.09997" />
|
||||
<path
|
||||
id="line122"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2110.1899,641.87 6.21,-2.71003" />
|
||||
<path
|
||||
id="line124"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2110.73,645.65997 5.6699,-0.76996" />
|
||||
</g>
|
||||
<g
|
||||
id="g144"
|
||||
style="display:inline">
|
||||
<path
|
||||
class="st1"
|
||||
d="m 2622.6,730.38 c -16.73,59.47 -75.78,94.88 -131.9,79.09 -56.12,-15.79 -88.06,-76.78 -71.33,-136.25 16.73,-59.47 85.78,-130.42 141.91,-114.65 56.13,15.78 78.06,112.34 61.33,171.81 z"
|
||||
id="path128" />
|
||||
<path
|
||||
id="polyline130"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2526.72,808.32 -19.3,-8.53 14.08,-2.84 -4.43,-10.78 21,11.07 7.37,-26.4 v 23.28 l 9.65,-4.54 3.42,9.09 29.65,-12.83" />
|
||||
<path
|
||||
id="polyline132"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2483.57,807.18 7.39,-8.51 h -7.39 l -11.35,-27.83 -3.4,19.3 -24.85,-9.34" />
|
||||
<path
|
||||
id="polyline134"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2540.33,802.07 -6.24,26.11 13.7,22.14 h -33.56" />
|
||||
<path
|
||||
id="polyline136"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2474.91,792.8 -41.44,23.07 -20.44,-27.62" />
|
||||
<path
|
||||
id="polyline138"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2442.6,625.17 -41.24,-17.54 29.33,38.03 -46.62,7.54 38.32,10.67" />
|
||||
<path
|
||||
id="polyline140"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2451.55,613.01 h 15.91 l -15.91,16.47 23.36,6.27 -23.36,6.12 3.53,26.65 -12.48,-4.65 7.01,39.68 -16.39,-24.32 -13.85,-6.02" />
|
||||
<path
|
||||
id="line142"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2433.47,815.87 10.5,15.17999" />
|
||||
</g>
|
||||
<path
|
||||
class="st1"
|
||||
d="M428.41,145.55s72.28-49.69,79.06,0,50.59,33.88,60.31,45.18,3.01,76.15,3.01,76.15"
|
||||
id="path146" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M580.92,311.58s66.44,290.37,403.14,14.59C1320.76,50.39,1411.5,55.29,1509.05,111.8s150.6,292.51,350.69,275.1c159.64-13.89,206.27-127.28,257.03-198.72,150.96-212.44,403.89,36.16,528.72-4.86,11.32-3.72,13.63-2.75,27.71-13.95,7.98-6.35,14.2-16.92,13.24-29.67-.87-11.47-12.82-21.2-38.56-12.99-51.47,16.41-129.03,129.9-54.86,249.23"
|
||||
id="path148" />
|
||||
<path
|
||||
id="polyline150"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1232.18,144.76 74.97,63.5 2.21,-103.55" />
|
||||
<path
|
||||
id="polyline152"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1368.27,90.42 77.41,72.83 8.09,-72.34" />
|
||||
<path
|
||||
id="polyline154"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1509.05,111.79 -7.24,97.63 61,-40.79" />
|
||||
<path
|
||||
id="polyline156"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1586.65,201.31 -27.13,91.75 67.78,-28.37" />
|
||||
<path
|
||||
id="polyline158"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1651.86,295.15 -21.04,87.47 76.76,-41.85" />
|
||||
<path
|
||||
id="polyline160"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1731.64,357.63 17.47,94.15 62.36,-65.71" />
|
||||
<path
|
||||
id="polyline162"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1859.74,386.9 53.5,71.31 22.26,-87.89" />
|
||||
<path
|
||||
id="polyline164"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 1977.79,349.46 73.65,47.81 -17.18,-95.93" />
|
||||
<path
|
||||
id="polyline166"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2061.96,266.82 93.23,12.94 -38.42,-91.58" />
|
||||
<path
|
||||
id="polyline168"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2158.71,141.59 73.9,47.43 3.02,-82.02" />
|
||||
<path
|
||||
id="polyline170"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2295.6,104.74 29.35,90.22 55.17,-76.77" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2826.74,409.03s-39.7,10.62-34.86,56.63l21.48-12.99,13.38,18.12,12.51-19.18,21.79,16.7s12-44.65-34.3-59.28h0Z"
|
||||
id="path172" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2791.88,465.65s-43.45,77.18-35.66,103.45,25.72,10.53,29.68,3.41c9.73-17.51,23.35-77.97,26.27-106.85"
|
||||
id="path174" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2812.17,465.65s-25.82,106.37-20.7,121.93,35.29,20.43,43.56,1.95,0-117.3,0-117.3"
|
||||
id="path176" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2857.91,468.3s30.19,82.3,19.92,101.53c-10.27,19.24-28.25,17.1-35.09,2.12s-7.7-99.72-7.7-99.72"
|
||||
id="path178" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2819.37,412.01c-3.44,3.21-3.43-65.85,64.52-54.06s36.95,194.18,36.95,194.18l-48.77,328.54h-18.49s59.93-324.51,63.42-365.63c6.1-72.1-1.5-145.18-40.18-148.41-49.73-4.16-41.78,45.61-41.78,45.61"
|
||||
id="path180" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2894.91,726.81l27.24-16.55s.85-93.33,94.02-79.28c0,0,7.02,88.6-87.97,87.75l-35.77,24.81"
|
||||
id="path182" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2867.07,806.97l-19.47-31.49s31.34-87.25-67.76-95.72c0,0-28.8,92.33,58.87,98.26l23.73,40.66"
|
||||
id="path184" />
|
||||
<path
|
||||
id="line186"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 3002.6201,643.34003 -67.77,65.21997" />
|
||||
<path
|
||||
id="polyline188"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2990.05,641.46 -0.93,14.86 15.53,-1.69" />
|
||||
<path
|
||||
id="polyline190"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2976.6,641.46 v 26.92 l 26.02,-2.01" />
|
||||
<path
|
||||
id="polyline192"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2962,648.89 1.32,32.27 32.46,-1.4" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2947.11,660.93l4.82,31.2s31.83.85,31.83,0"
|
||||
id="path194" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2934.85,681.16l5.12,22.47s25.17.62,25.46,0"
|
||||
id="path196" />
|
||||
<path
|
||||
id="line198"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2784.79,686.53003 53.5301,82.03998" />
|
||||
<path
|
||||
id="polyline200"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2783.07,700.44 h 10.79 l 2.95,-11.45" />
|
||||
<path
|
||||
id="polyline202"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2781.35,713.32 h 20.92 l 5.7,-20.79" />
|
||||
<path
|
||||
id="polyline204"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2784.79,727.55 h 26.76 l 6.67,-27.11" />
|
||||
<path
|
||||
id="polyline206"
|
||||
style="display:inline;fill:none;stroke:#000000"
|
||||
class="st1"
|
||||
d="m 2789.94,741.95 h 31.01 l 8.21,-33.39" />
|
||||
<path
|
||||
id="polyline208"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2803.97,757.12 h 26.88 l 7.47,-33.49" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2908.76,130s-47.51,156.81,25.31,176.6c0,0,26.26,6.73,51.71,0l-77.02-176.6Z"
|
||||
id="path210" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2963.82,256.24l55.4-136.3s39.79,123.35,15.92,156.78-49.36,29.87-49.36,29.87"
|
||||
id="path212" />
|
||||
<path
|
||||
id="polyline214"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 2926.22,170.03 33.31,-54.07 41.11,49.7" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2946.46,308.18s4.77,22.77,24.65,22.51c17.32-.23,23.81-13.52,24.65-24.88"
|
||||
id="path216" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2971.1,330.7s89.96,254.5,59.46,549.97H167.52"
|
||||
id="path218" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M2982.21,328.28s89.86,223.58,65.72,552.39c0,0,84.28-151.25,84.28-227.75s-47.44-261.07-35.58-324.64c0,0-58.52,13.94-39.42,255.45"
|
||||
id="path220" />
|
||||
<g
|
||||
id="g274"
|
||||
style="display:inline">
|
||||
<path
|
||||
id="ellipse222"
|
||||
class="st0"
|
||||
transform="rotate(-66.78,296.31639,619.81639)"
|
||||
style="fill:none;stroke:#000000;stroke-width:1px"
|
||||
d="m 320.68001,619.82001 a 24.360001,36.130001 0 0 1 -24.36,36.13 24.360001,36.130001 0 0 1 -24.36,-36.13 24.360001,36.130001 0 0 1 24.36,-36.13 24.360001,36.130001 0 0 1 24.36,36.13 z" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 288.95,590.6 c 0,0 7.37,-53.85 0,-63.2 -3.7,-4.69 -25.07,-12.01 -8.11,63.2"
|
||||
id="path224" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 276.75,591.96 c 0,0 -7.05,-53.89 -16.63,-60.98 -4.8,-3.55 -27.35,-4.99 8.8,63.11"
|
||||
id="path226" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 266.24,595.31 c 0,0 -26.11,-47.67 -37.6,-50.8 -5.77,-1.57 -27.3,5.26 31.08,55.63"
|
||||
id="path228" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 259.96,602.4 c 0,0 -41.41,-35.21 -53.26,-34.03 -5.95,0.59 -23.62,14.66 48.89,40.87 0,0 -40.01,-4.43 -41.51,3.95 -1.5,8.38 3,14.18 9,15.28 6,1.1 36.88,-11.02 36.88,-11.02 l -32.38,20.12 c 0,0 -6,8 5.5,13.5 11.5,5.5 29.68,-19.99 29.68,-19.99"
|
||||
id="path230" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 299.7,593.32 c 0,0 24.74,-48.4 20.86,-59.66 -1.94,-5.65 -19.7,-19.62 -28.52,56.98"
|
||||
id="path232" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 313.08,597.51 c 0,0 35.13,-41.47 33.93,-53.32 -0.6,-5.95 -14.7,-23.6 -40.77,48.97"
|
||||
id="path234" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 323.69,607.04 c 0,0 43.28,-32.88 44.67,-44.71 0.7,-5.93 -9.24,-26.22 -50.41,38.97"
|
||||
id="path236" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 331.93,617.45 c 0,0 49.83,-21.72 53.98,-32.88 2.08,-5.6 -2.78,-27.66 -58.2,25.95"
|
||||
id="path238" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 335.13,628.47 c 0,0 54.26,-3.22 62,-12.26 3.88,-4.54 6.92,-26.93 -63.57,4.31"
|
||||
id="path240" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 333.33,638.91 c 0,0 49.93,21.48 60.91,16.87 5.51,-2.31 18.27,-20.96 -58.75,-24.68"
|
||||
id="path242" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 329.87,642.05 c 72.18,27.1 54.33,40.95 48.38,41.47 -11.86,1.03 -52.83,-34.69 -52.83,-34.69 0,0 21.31,23.26 17.75,30.76 -3.56,7.5 -17.06,4.22 -27.56,-27.4"
|
||||
id="path244" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 266.24,637.07 c 0,0 -24.6,13.69 -23.13,19.35 1.47,5.66 5.81,12.16 19.65,10.66 13.84,-1.5 11.62,-27.51 11.62,-27.51"
|
||||
id="path246" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 312.35,651.86 c 0,0 13.8,23.47 8.63,28.92 -5.17,5.45 -13.25,1.7 -16.72,-5.94 -1.65,-3.63 -3.31,-19.69 -1.81,-24.09"
|
||||
id="path248" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 279.02,644.81 c 0,0 -11.43,22.33 -8.58,27.55 2.48,4.55 9.1,7.06 16.09,1.61 6.46,-5.04 -0.25,-27.58 -0.25,-27.58"
|
||||
id="path250" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 292.04,648.11 c 0,0 -5.7,19.72 0,26.89 2.8,3.53 8.59,5.11 9.82,0 1.78,-7.4 -1.73,-24.25 -1.73,-24.25"
|
||||
id="path252" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 253.7,667.3 c 0,0 -39.13,90.39 -22.11,209.82"
|
||||
id="path254" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 261.97,667.3 c 0,0 -39.13,90.39 -22.11,209.82"
|
||||
id="path256" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 235.59,819.54 c 0,0 48.82,-112.23 107.59,-87.46 58.77,24.77 69.77,76.43 69.77,76.43 0,0 -25.41,11.86 -52.52,-5.08 -27.11,-16.94 -70.15,-27.95 -124.86,28.8"
|
||||
id="path258" />
|
||||
<path
|
||||
class="st1"
|
||||
d="m 227.31,772.21 c 0,0 -17.65,-95.85 -36.28,-105.16 -18.63,-9.31 -56.75,5.93 -56.75,5.93 0,0 -32.19,-2.55 -49.98,35.57 0,0 47.44,10.17 87.25,-4.23 0,0 37.84,0 55.77,90.64"
|
||||
id="path260" />
|
||||
<path
|
||||
id="circle262"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 278.81,604.83002 a 1.03,1.03 0 0 1 -1.03,1.03 1.03,1.03 0 0 1 -1.03,-1.03 1.03,1.03 0 0 1 1.03,-1.03 1.03,1.03 0 0 1 1.03,1.03 z" />
|
||||
<path
|
||||
id="circle264"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 278.03999,611.71997 a 1.3,1.3 0 0 1 -1.3,1.3 1.3,1.3 0 0 1 -1.3,-1.3 1.3,1.3 0 0 1 1.3,-1.3 1.3,1.3 0 0 1 1.3,1.3 z" />
|
||||
<path
|
||||
id="circle266"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 287.81001,611.78003 a 1.36,1.36 0 0 1 -1.36,1.36 1.36,1.36 0 0 1 -1.36,-1.36 1.36,1.36 0 0 1 1.36,-1.36 1.36,1.36 0 0 1 1.36,1.36 z" />
|
||||
<path
|
||||
id="circle268"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 295.30001,606.62 a 0.75999999,0.75999999 0 0 1 -0.76,0.76 0.75999999,0.75999999 0 0 1 -0.76,-0.76 0.75999999,0.75999999 0 0 1 0.76,-0.76 0.75999999,0.75999999 0 0 1 0.76,0.76 z" />
|
||||
<path
|
||||
id="circle270"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 286.45001,623.46997 a 1.22,1.22 0 0 1 -1.22,1.22 1.22,1.22 0 0 1 -1.22,-1.22 1.22,1.22 0 0 1 1.22,-1.22 1.22,1.22 0 0 1 1.22,1.22 z" />
|
||||
<path
|
||||
id="circle272"
|
||||
class="st1"
|
||||
style="fill:none;stroke:#000000"
|
||||
d="m 297.66001,625.87 a 1.1799999,1.1799999 0 0 1 -1.18,1.18 1.1799999,1.1799999 0 0 1 -1.18,-1.18 1.1799999,1.1799999 0 0 1 1.18,-1.18 1.1799999,1.1799999 0 0 1 1.18,1.18 z" />
|
||||
</g>
|
||||
<g
|
||||
id="g284">
|
||||
<path
|
||||
class="st1"
|
||||
d="M231.04,397.37s-61.7-60.98-55.77-12.8,42.72,38.54,56.44,35.68c0,0-29.62,18.49-20.7,30.22s30.96,14.48,33.85,5.15c2.89-9.33.58-22.09.58-22.09"
|
||||
id="path276" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M261.15,449.43c-28.02-40.68-25.21-70.98-25.21-70.98,0,0-13.97-6.09-20.88-21.17"
|
||||
id="path278" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M246.2,391.31s-3.68-53.04,13.76-54.35c17.45-1.31,35.56,13.22,35.41,34.75s-38.86,39.05-38.86,39.05c0,0,33.21-8.14,38.5,5.15s-7.41,32.93-17.09,29.25-25.24-24.7-25.24-24.7"
|
||||
id="path280" />
|
||||
<path
|
||||
class="st1"
|
||||
d="M213.16,365.46s9.86,11.91,19.57,13.64"
|
||||
id="path282" />
|
||||
</g>
|
||||
<eggbot
|
||||
layer="0"
|
||||
node="0"
|
||||
lastpath="0"
|
||||
lastpathnc="0"
|
||||
totaldeltax="0"
|
||||
totaldeltay="0" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 23 KiB |
161
svg/holger.svg
Normal file
161
svg/holger.svg
Normal file
@@ -0,0 +1,161 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 3200 1024"
|
||||
width="3200"
|
||||
height="1024"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg6435"
|
||||
sodipodi:docname="holger.svg"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs6439">
|
||||
<rect
|
||||
x="527.87384"
|
||||
y="605.33038"
|
||||
width="1085.6606"
|
||||
height="402.98206"
|
||||
id="rect7043" />
|
||||
<rect
|
||||
x="656.15399"
|
||||
y="649.70941"
|
||||
width="550.19073"
|
||||
height="212.89944"
|
||||
id="rect7037" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview6437"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.22269721"
|
||||
inkscape:cx="222.2749"
|
||||
inkscape:cy="624.16589"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1008"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg6435" />
|
||||
<metadata
|
||||
id="metadata1">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:date>2026-02-26T20:25:37.207Z</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>EggBot Sorbische Eier</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>Copyright 2026 André Fiedler</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>EggBot Sorbische Eier</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>eggi-1652987163-1.3.40</dc:identifier>
|
||||
<dc:source>https://eggbot.app/</dc:source>
|
||||
<dc:relation>https://eggbot.app/</dc:relation>
|
||||
<dc:language>de</dc:language>
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>sorbian</rdf:li>
|
||||
<rdf:li>eggbot</rdf:li>
|
||||
<rdf:li>ornament</rdf:li>
|
||||
<rdf:li>version-1.3.40</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
<dc:coverage>2048x1024px</dc:coverage>
|
||||
<dc:description>Generated with EggBot Sorbische Eier using eggbot-app 1.3.40</dc:description>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>EggBot Sorbische Eier</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
aria-label="Holger's"
|
||||
transform="matrix(1.9550597,0,0,0.6333943,-339.16516,-218.21761)"
|
||||
id="text6983"
|
||||
style="font-size:267.284px;vector-effect:non-scaling-stroke;stroke:#000000;stroke-width:1;stroke-dasharray:none;-inkscape-stroke:hairline">
|
||||
<path
|
||||
d="m 337.82058,591.41632 h -24.05556 v -89.00557 h -97.82594 v 89.00557 H 191.88352 V 400.57555 h 24.05556 v 80.71976 h 97.82594 v -80.71976 h 24.05556 z"
|
||||
id="path7791"
|
||||
style="vector-effect:non-scaling-stroke;stroke-width:1;stroke-dasharray:none;-inkscape-stroke:hairline" />
|
||||
<path
|
||||
d="m 511.28769,519.51693 q 0,35.54877 -18.17531,55.0605 -17.90803,19.51173 -48.64569,19.51173 -18.97716,0 -33.94507,-8.55309 -14.70062,-8.82037 -23.25371,-25.39198 -8.55308,-16.83889 -8.55308,-40.62716 0,-35.54878 17.90802,-54.79322 17.90803,-19.24445 48.64569,-19.24445 19.51173,0 34.21235,8.82037 14.96791,8.55309 23.25371,25.1247 8.55309,16.30432 8.55309,40.0926 z m -108.25002,0 q 0,25.39198 9.88951,40.35988 10.15679,14.70062 32.07408,14.70062 21.65,0 31.80679,-14.70062 10.15679,-14.9679 10.15679,-40.35988 0,-25.39198 -10.15679,-39.82532 -10.15679,-14.43334 -32.07408,-14.43334 -21.91728,0 -31.80679,14.43334 -9.88951,14.43334 -9.88951,39.82532 z"
|
||||
id="path7793"
|
||||
style="vector-effect:non-scaling-stroke;stroke-width:1;stroke-dasharray:none;-inkscape-stroke:hairline" />
|
||||
<path
|
||||
d="M 571.96102,591.41632 H 548.44003 V 388.28048 h 23.52099 z"
|
||||
id="path7795"
|
||||
style="vector-effect:non-scaling-stroke;stroke-width:1;stroke-dasharray:none;-inkscape-stroke:hairline" />
|
||||
<path
|
||||
d="m 668.18312,445.47926 q 14.16605,0 25.39198,5.34568 11.49321,5.34568 19.51173,16.30432 h 1.33642 l 3.20741,-18.97716 h 18.70988 v 145.66978 q 0,30.73766 -15.76975,46.24013 -15.50248,15.50247 -48.37841,15.50247 -31.53951,0 -51.58581,-9.08766 v -21.65 q 21.11544,11.22593 52.92223,11.22593 18.4426,0 28.86667,-10.95865 10.69136,-10.69136 10.69136,-29.40124 v -5.61296 q 0,-3.20741 0.26729,-9.08766 0.26728,-6.14753 0.53457,-8.55308 h -1.06914 q -14.43334,21.65 -44.36914,21.65 -27.79754,0 -43.5673,-19.51173 -15.50247,-19.51173 -15.50247,-54.52594 0,-34.21235 15.50247,-54.25865 15.76976,-20.31358 43.30001,-20.31358 z m 3.20741,19.77901 q -17.90803,0 -27.79754,14.43334 -9.8895,14.16605 -9.8895,40.62717 0,26.46111 9.62222,40.62717 9.88951,13.89876 28.59939,13.89876 21.65,0 31.53951,-11.49321 9.88951,-11.76049 9.88951,-37.68704 v -5.61297 q 0,-29.40124 -10.15679,-41.96358 -10.1568,-12.82964 -31.8068,-12.82964 z"
|
||||
id="path7797"
|
||||
style="vector-effect:non-scaling-stroke;stroke-width:1;stroke-dasharray:none;-inkscape-stroke:hairline" />
|
||||
<path
|
||||
d="m 837.10656,445.47926 q 18.44259,0 31.53951,8.01852 13.3642,8.01852 20.31358,22.71914 7.21667,14.43333 7.21667,33.94507 v 14.16605 h -98.09323 q 0.53457,24.32284 12.29507,37.15247 12.02778,12.56235 33.4105,12.56235 13.63148,0 24.05556,-2.40555 10.69136,-2.67284 21.91729,-7.48396 v 20.58087 q -10.95865,4.81111 -21.65001,6.94938 -10.69136,2.40556 -25.39198,2.40556 -20.31358,0 -36.08334,-8.2858 -15.50247,-8.28581 -24.32284,-24.59013 -8.55309,-16.57161 -8.55309,-40.35988 0,-23.521 7.75124,-40.35989 8.01852,-16.83889 22.18457,-25.92655 14.43333,-9.08765 33.4105,-9.08765 z m -0.26729,19.24445 q -16.83889,0 -26.7284,10.95864 -9.62222,10.69136 -11.49321,29.93581 h 72.96853 q -0.26728,-18.17531 -8.55308,-29.40124 -8.28581,-11.49321 -26.19384,-11.49321 z"
|
||||
id="path7799"
|
||||
style="vector-effect:non-scaling-stroke;stroke-width:1;stroke-dasharray:none;-inkscape-stroke:hairline" />
|
||||
<path
|
||||
d="m 999.3477,445.47926 q 4.0093,0 8.5531,0.53457 4.8111,0.26728 8.2858,1.06913 l -2.9401,21.65001 q -3.4747,-0.80186 -7.7513,-1.33642 -4.0092,-0.53457 -7.75121,-0.53457 -10.95864,0 -20.58087,6.14753 -9.62222,5.88025 -15.50247,16.83889 -5.61296,10.69136 -5.61296,25.1247 v 76.44322 H 932.5267 V 448.1521 h 19.24444 l 2.67284,26.19383 h 1.06914 q 6.94938,-11.7605 17.90803,-20.31358 10.95864,-8.55309 25.92655,-8.55309 z"
|
||||
id="path7801"
|
||||
style="vector-effect:non-scaling-stroke;stroke-width:1;stroke-dasharray:none;-inkscape-stroke:hairline" />
|
||||
<path
|
||||
d="m 1073.653,400.57555 -5.3457,68.95927 h -14.7006 l -5.3457,-68.95927 z"
|
||||
id="path7803"
|
||||
style="vector-effect:non-scaling-stroke;stroke-width:1;stroke-dasharray:none;-inkscape-stroke:hairline" />
|
||||
<path
|
||||
d="m 1199.0091,551.85829 q 0,20.84815 -15.5025,31.53951 -15.5025,10.69136 -41.6963,10.69136 -14.9679,0 -25.9266,-2.40556 -10.6913,-2.40555 -18.9771,-6.6821 v -21.38271 q 8.5531,4.27654 20.5808,8.01852 12.2951,3.47469 24.8575,3.47469 17.908,0 25.9265,-5.61297 8.0185,-5.88024 8.0185,-15.50247 0,-5.34568 -2.9401,-9.62222 -2.9401,-4.27655 -10.6914,-8.55309 -7.4839,-4.27654 -21.65,-9.62222 -13.8987,-5.34568 -23.7882,-10.69136 -9.8895,-5.34568 -15.2352,-12.82964 -5.3457,-7.48395 -5.3457,-19.24444 0,-18.17532 14.7006,-28.06482 14.9679,-9.88951 39.0235,-9.88951 13.0969,0 24.3228,2.67284 11.4932,2.40555 21.3827,6.94938 l -8.0185,18.70988 q -9.0876,-3.74197 -18.9771,-6.41481 -9.8895,-2.67284 -20.3136,-2.67284 -14.4334,0 -22.1846,4.81111 -7.4839,4.54383 -7.4839,12.56235 0,5.88024 3.4746,10.15679 3.4747,4.00926 11.4933,8.01852 8.2858,3.74197 21.9172,9.08765 13.6315,5.0784 23.2538,10.42408 9.6222,5.34568 14.7006,13.09692 5.0784,7.48395 5.0784,18.97716 z"
|
||||
id="path7805"
|
||||
style="vector-effect:non-scaling-stroke;stroke-width:1;stroke-dasharray:none;-inkscape-stroke:hairline" />
|
||||
</g>
|
||||
<g
|
||||
aria-label="Egg"
|
||||
transform="matrix(2.8193627,0,0,1.1226094,4.5464998,-430.01989)"
|
||||
id="text7049"
|
||||
style="font-size:411.886px;vector-effect:non-scaling-stroke;stroke:#000000;stroke-width:1;stroke-dasharray:none;-inkscape-stroke:hairline">
|
||||
<path
|
||||
d="M 563.96012,1117.9706 H 399.61761 V 823.88399 h 164.34251 v 32.53899 H 436.68735 v 91.85057 h 119.85882 v 32.12711 H 436.68735 v 105.03094 h 127.27277 z"
|
||||
id="path7808"
|
||||
style="vector-effect:non-scaling-stroke;stroke-width:1;stroke-dasharray:none;-inkscape-stroke:hairline" />
|
||||
<path
|
||||
d="m 701.94183,893.08083 q 21.82996,0 39.12917,8.23772 17.7111,8.23772 30.06768,25.12505 h 2.05943 l 4.94263,-29.24391 h 28.83202 v 224.47791 q 0,47.3668 -24.30127,71.2562 -23.88939,23.8894 -74.55137,23.8894 -48.60254,0 -79.49399,-14.0041 v -33.3628 q 32.53899,17.2992 81.55342,17.2992 28.42014,0 44.48369,-16.8873 16.47544,-16.4754 16.47544,-45.3074 v -8.6496 q 0,-4.9427 0.41189,-14.0042 0.41188,-9.4734 0.82377,-13.1803 h -1.64755 q -22.24184,33.3627 -68.37307,33.3627 -42.83614,0 -67.13742,-30.0676 -23.88938,-30.0677 -23.88938,-84.0248 0,-52.72138 23.88938,-83.61283 24.30128,-31.30334 66.72553,-31.30334 z m 4.94264,30.47957 q -27.59637,0 -42.83615,22.24184 -15.23978,21.82996 -15.23978,62.60666 0,40.7767 14.8279,62.6067 15.23978,21.418 44.0718,21.418 33.36276,0 48.60254,-17.711 15.23979,-18.123 15.23979,-58.076 v -8.6496 q 0,-45.30744 -15.65167,-64.66608 -15.65167,-19.77052 -49.01443,-19.77052 z"
|
||||
id="path7810"
|
||||
style="vector-effect:non-scaling-stroke;stroke-width:1;stroke-dasharray:none;-inkscape-stroke:hairline" />
|
||||
<path
|
||||
d="m 955.25162,893.08083 q 21.82995,0 39.12917,8.23772 17.71111,8.23772 30.06771,25.12505 h 2.0594 l 4.9426,-29.24391 h 28.832 v 224.47791 q 0,47.3668 -24.3012,71.2562 -23.8894,23.8894 -74.55139,23.8894 -48.60255,0 -79.494,-14.0041 v -33.3628 q 32.53899,17.2992 81.55343,17.2992 28.42013,0 44.48366,-16.8873 16.4755,-16.4754 16.4755,-45.3074 v -8.6496 q 0,-4.9427 0.4118,-14.0042 0.4119,-9.4734 0.8238,-13.1803 h -1.6475 q -22.2419,33.3627 -68.3731,33.3627 -42.83614,0 -67.13741,-30.0676 -23.88939,-30.0677 -23.88939,-84.0248 0,-52.72138 23.88939,-83.61283 24.30127,-31.30334 66.72553,-31.30334 z m 4.94263,30.47957 q -27.59636,0 -42.83614,22.24184 -15.23978,21.82996 -15.23978,62.60666 0,40.7767 14.82789,62.6067 15.23978,21.418 44.0718,21.418 33.36277,0 48.60258,-17.711 15.2397,-18.123 15.2397,-58.076 v -8.6496 q 0,-45.30744 -15.6516,-64.66608 -15.65169,-19.77052 -49.01445,-19.77052 z"
|
||||
id="path7812"
|
||||
style="vector-effect:non-scaling-stroke;stroke-width:1;stroke-dasharray:none;-inkscape-stroke:hairline" />
|
||||
</g>
|
||||
<eggbot
|
||||
layer="12345"
|
||||
node="0"
|
||||
lastpath="0"
|
||||
lastpathnc="0"
|
||||
totaldeltax="0"
|
||||
totaldeltay="0" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
979
svg/rect.svg
Normal file
979
svg/rect.svg
Normal file
@@ -0,0 +1,979 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 3200 1024"
|
||||
width="3200"
|
||||
height="1024"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg6435"
|
||||
sodipodi:docname="rect.svg"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs6439">
|
||||
<rect
|
||||
x="527.87384"
|
||||
y="605.33038"
|
||||
width="1085.6606"
|
||||
height="402.98206"
|
||||
id="rect7043" />
|
||||
<rect
|
||||
x="656.15399"
|
||||
y="649.70941"
|
||||
width="550.19073"
|
||||
height="212.89944"
|
||||
id="rect7037" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview6437"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.31494141"
|
||||
inkscape:cx="1592.3597"
|
||||
inkscape:cy="650.91473"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1008"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg6435" />
|
||||
<metadata
|
||||
id="metadata1">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:date>2026-02-26T20:25:37.207Z</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>EggBot Sorbische Eier</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>Copyright 2026 André Fiedler</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>EggBot Sorbische Eier</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>eggi-1652987163-1.3.40</dc:identifier>
|
||||
<dc:source>https://eggbot.app/</dc:source>
|
||||
<dc:relation>https://eggbot.app/</dc:relation>
|
||||
<dc:language>de</dc:language>
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>sorbian</rdf:li>
|
||||
<rdf:li>eggbot</rdf:li>
|
||||
<rdf:li>ornament</rdf:li>
|
||||
<rdf:li>version-1.3.40</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
<dc:coverage>2048x1024px</dc:coverage>
|
||||
<dc:description>Generated with EggBot Sorbische Eier using eggbot-app 1.3.40</dc:description>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>EggBot Sorbische Eier</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<eggbot
|
||||
layer="12345"
|
||||
node="0"
|
||||
lastpath="0"
|
||||
lastpathnc="0"
|
||||
totaldeltax="0"
|
||||
totaldeltay="0" />
|
||||
<rect
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-width:0.468004;stroke-dasharray:none;-inkscape-stroke:hairline"
|
||||
id="rect33437"
|
||||
width="12.822335"
|
||||
height="959.19885"
|
||||
x="40.943718"
|
||||
y="35.52866"
|
||||
inkscape:tile-cx="47.354886"
|
||||
inkscape:tile-cy="515.12809"
|
||||
inkscape:tile-w="12.822335"
|
||||
inkscape:tile-h="959.19885"
|
||||
inkscape:tile-x0="40.943718"
|
||||
inkscape:tile-y0="35.52866" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
id="use37434" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(25.683137)"
|
||||
id="use37436" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(51.366274)"
|
||||
id="use37438" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(77.049411)"
|
||||
id="use37440" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(102.73255)"
|
||||
id="use37442" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(128.41569)"
|
||||
id="use37444" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(154.09882)"
|
||||
id="use37446" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(179.78196)"
|
||||
id="use37448" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(205.4651)"
|
||||
id="use37450" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(231.14823)"
|
||||
id="use37452" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(256.83137)"
|
||||
id="use37454" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(282.51451)"
|
||||
id="use37456" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(308.19764)"
|
||||
id="use37458" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(333.88078)"
|
||||
id="use37460" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(359.56392)"
|
||||
id="use37462" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(385.24706)"
|
||||
id="use37464" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(410.93019)"
|
||||
id="use37466" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(436.61333)"
|
||||
id="use37468" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(462.29647)"
|
||||
id="use37470" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(487.9796)"
|
||||
id="use37472" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(513.66274)"
|
||||
id="use37474" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(539.34588)"
|
||||
id="use37476" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(565.02901)"
|
||||
id="use37478" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(590.71215)"
|
||||
id="use37480" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(616.39529)"
|
||||
id="use37482" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(642.07843)"
|
||||
id="use37484" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(667.76156)"
|
||||
id="use37486" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(693.4447)"
|
||||
id="use37488" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(719.12784)"
|
||||
id="use37490" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(744.81097)"
|
||||
id="use37492" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(770.49411)"
|
||||
id="use37494" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(796.17725)"
|
||||
id="use37496" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(821.86038)"
|
||||
id="use37498" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(847.54352)"
|
||||
id="use37500" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(873.22666)"
|
||||
id="use37502" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(898.9098)"
|
||||
id="use37504" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(924.59293)"
|
||||
id="use37506" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(950.27607)"
|
||||
id="use37508" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(975.95921)"
|
||||
id="use37510" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1001.6423)"
|
||||
id="use37512" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1027.3255)"
|
||||
id="use37514" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1053.0086)"
|
||||
id="use37516" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1078.6918)"
|
||||
id="use37518" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1104.3749)"
|
||||
id="use37520" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1130.058)"
|
||||
id="use37522" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1155.7412)"
|
||||
id="use37524" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1181.4243)"
|
||||
id="use37526" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1207.1074)"
|
||||
id="use37528" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1232.7906)"
|
||||
id="use37530" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1258.4737)"
|
||||
id="use37532" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1284.1569)"
|
||||
id="use37534" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1309.84)"
|
||||
id="use37536" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1335.5231)"
|
||||
id="use37538" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1361.2063)"
|
||||
id="use37540" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1386.8894)"
|
||||
id="use37542" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1412.5725)"
|
||||
id="use37544" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1438.2557)"
|
||||
id="use37546" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1463.9388)"
|
||||
id="use37548" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1489.6219)"
|
||||
id="use37550" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1515.3051)"
|
||||
id="use37552" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1540.9882)"
|
||||
id="use37554" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1566.6714)"
|
||||
id="use37556" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1592.3545)"
|
||||
id="use37558" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1618.0376)"
|
||||
id="use37560" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1643.7208)"
|
||||
id="use37562" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1669.4039)"
|
||||
id="use37564" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1695.087)"
|
||||
id="use37566" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1720.7702)"
|
||||
id="use37568" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1746.4533)"
|
||||
id="use37570" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1772.1365)"
|
||||
id="use37572" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1797.8196)"
|
||||
id="use37574" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1823.5027)"
|
||||
id="use37576" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1849.1859)"
|
||||
id="use37578" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1874.869)"
|
||||
id="use37580" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1900.5521)"
|
||||
id="use37582" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1926.2353)"
|
||||
id="use37584" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1951.9184)"
|
||||
id="use37586" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(1977.6015)"
|
||||
id="use37588" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2003.2847)"
|
||||
id="use37590" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2028.9678)"
|
||||
id="use37592" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2054.651)"
|
||||
id="use37594" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2080.3341)"
|
||||
id="use37596" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2106.0172)"
|
||||
id="use37598" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2131.7004)"
|
||||
id="use37600" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2157.3835)"
|
||||
id="use37602" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2183.0666)"
|
||||
id="use37604" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2208.7498)"
|
||||
id="use37606" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2234.4329)"
|
||||
id="use37608" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2260.1161)"
|
||||
id="use37610" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2285.7992)"
|
||||
id="use37612" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2311.4823)"
|
||||
id="use37614" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2337.1655)"
|
||||
id="use37616" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2362.8486)"
|
||||
id="use37618" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2388.5317)"
|
||||
id="use37620" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2414.2149)"
|
||||
id="use37622" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2439.898)"
|
||||
id="use37624" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2465.5812)"
|
||||
id="use37626" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2491.2643)"
|
||||
id="use37628" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2516.9474)"
|
||||
id="use37630" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2542.6306)"
|
||||
id="use37632" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2568.3137)"
|
||||
id="use37634" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2593.9968)"
|
||||
id="use37636" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2619.68)"
|
||||
id="use37638" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2645.3631)"
|
||||
id="use37640" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2671.0462)"
|
||||
id="use37642" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2696.7294)"
|
||||
id="use37644" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2722.4125)"
|
||||
id="use37646" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2748.0957)"
|
||||
id="use37648" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2773.7788)"
|
||||
id="use37650" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2799.4619)"
|
||||
id="use37652" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2825.1451)"
|
||||
id="use37654" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2850.8282)"
|
||||
id="use37656" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2876.5113)"
|
||||
id="use37658" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2902.1945)"
|
||||
id="use37660" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2927.8776)"
|
||||
id="use37662" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2953.5608)"
|
||||
id="use37664" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(2979.2439)"
|
||||
id="use37666" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(3004.927)"
|
||||
id="use37668" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(3030.6102)"
|
||||
id="use37670" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(3056.2933)"
|
||||
id="use37672" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(3081.9764)"
|
||||
id="use37674" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(3107.6596)"
|
||||
id="use37676" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect33437"
|
||||
xlink:href="#rect33437"
|
||||
transform="translate(3133.3427)"
|
||||
id="use37678" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 23 KiB |
375
svg/test-stars.svg
Normal file
375
svg/test-stars.svg
Normal file
@@ -0,0 +1,375 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 3200 1024"
|
||||
width="3200"
|
||||
height="1024"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg6435"
|
||||
sodipodi:docname="test-stars.svg"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs6439">
|
||||
<rect
|
||||
x="527.87384"
|
||||
y="605.33038"
|
||||
width="1085.6606"
|
||||
height="402.98206"
|
||||
id="rect7043" />
|
||||
<rect
|
||||
x="656.15399"
|
||||
y="649.70941"
|
||||
width="550.19073"
|
||||
height="212.89944"
|
||||
id="rect7037" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview6437"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.22269721"
|
||||
inkscape:cx="226.7653"
|
||||
inkscape:cy="624.16588"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="2088"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg6435" />
|
||||
<metadata
|
||||
id="metadata1">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:date>2026-02-26T20:25:37.207Z</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>EggBot Sorbische Eier</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>Copyright 2026 André Fiedler</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>EggBot Sorbische Eier</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>eggi-1652987163-1.3.40</dc:identifier>
|
||||
<dc:source>https://eggbot.app/</dc:source>
|
||||
<dc:relation>https://eggbot.app/</dc:relation>
|
||||
<dc:language>de</dc:language>
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>sorbian</rdf:li>
|
||||
<rdf:li>eggbot</rdf:li>
|
||||
<rdf:li>ornament</rdf:li>
|
||||
<rdf:li>version-1.3.40</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
<dc:coverage>2048x1024px</dc:coverage>
|
||||
<dc:description>Generated with EggBot Sorbische Eier using eggbot-app 1.3.40</dc:description>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>EggBot Sorbische Eier</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<eggbot
|
||||
layer="0"
|
||||
node="0"
|
||||
lastpath="0"
|
||||
lastpathnc="0"
|
||||
totaldeltax="0"
|
||||
totaldeltay="0" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:bevel;-inkscape-stroke:hairline;paint-order:fill markers stroke"
|
||||
id="path1577"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="143.94885"
|
||||
sodipodi:cy="162.33527"
|
||||
sodipodi:r1="281.11676"
|
||||
sodipodi:r2="140.55838"
|
||||
sodipodi:arg1="0.60507974"
|
||||
sodipodi:arg2="1.2333983"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 375.15523,322.24226 190.47831,294.96885 63.314968,431.63957 32.185209,247.57344 -137.092,168.86749 28.345668,82.381768 50.889936,-102.9319 184.2658,27.683114 367.47614,-8.141086 284.46928,159.06916 Z"
|
||||
inkscape:transform-center-x="39.552366"
|
||||
inkscape:transform-center-y="3.0857472"
|
||||
transform="matrix(1.5873511,0,0,1.5286783,1415.4845,238.82281)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:bevel;-inkscape-stroke:hairline;paint-order:fill markers stroke"
|
||||
id="path1577-3"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="143.94885"
|
||||
sodipodi:cy="162.33527"
|
||||
sodipodi:r1="281.11676"
|
||||
sodipodi:r2="140.55838"
|
||||
sodipodi:arg1="0.60507974"
|
||||
sodipodi:arg2="1.2333983"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 375.15523,322.24226 190.47831,294.96885 63.314968,431.63957 32.185209,247.57344 -137.092,168.86749 28.345668,82.381768 50.889936,-102.9319 184.2658,27.683114 367.47614,-8.141086 284.46928,159.06916 Z"
|
||||
inkscape:transform-center-x="18.782775"
|
||||
inkscape:transform-center-y="1.4067283"
|
||||
transform="matrix(0.75380592,0,0,0.6968924,776.59278,667.72404)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:bevel;-inkscape-stroke:hairline;paint-order:fill markers stroke"
|
||||
id="path1577-3-7"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="143.94885"
|
||||
sodipodi:cy="162.33527"
|
||||
sodipodi:r1="281.11676"
|
||||
sodipodi:r2="140.55838"
|
||||
sodipodi:arg1="0.60507974"
|
||||
sodipodi:arg2="1.2333983"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 375.15523,322.24226 190.47831,294.96885 63.314968,431.63957 32.185209,247.57344 -137.092,168.86749 28.345668,82.381768 50.889936,-102.9319 184.2658,27.683114 367.47614,-8.141086 284.46928,159.06916 Z"
|
||||
inkscape:transform-center-x="18.782775"
|
||||
inkscape:transform-center-y="1.4067283"
|
||||
transform="matrix(0.75380592,0,0,0.6968924,708.01302,147.69442)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:bevel;-inkscape-stroke:hairline;paint-order:fill markers stroke"
|
||||
id="path1577-3-5"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="143.94885"
|
||||
sodipodi:cy="162.33527"
|
||||
sodipodi:r1="281.11676"
|
||||
sodipodi:r2="140.55838"
|
||||
sodipodi:arg1="0.60507974"
|
||||
sodipodi:arg2="1.2333983"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 375.15523,322.24226 190.47831,294.96885 63.314968,431.63957 32.185209,247.57344 -137.092,168.86749 28.345668,82.381768 50.889936,-102.9319 184.2658,27.683114 367.47614,-8.141086 284.46928,159.06916 Z"
|
||||
inkscape:transform-center-x="18.782775"
|
||||
inkscape:transform-center-y="1.4067283"
|
||||
transform="matrix(0.75380592,0,0,0.6968924,284.23914,674.5927)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:bevel;-inkscape-stroke:hairline;paint-order:fill markers stroke"
|
||||
id="path1577-3-7-3"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="143.94885"
|
||||
sodipodi:cy="162.33527"
|
||||
sodipodi:r1="281.11676"
|
||||
sodipodi:r2="140.55838"
|
||||
sodipodi:arg1="0.60507974"
|
||||
sodipodi:arg2="1.2333983"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 375.15523,322.24226 190.47831,294.96885 63.314968,431.63957 32.185209,247.57344 -137.092,168.86749 28.345668,82.381768 50.889936,-102.9319 184.2658,27.683114 367.47614,-8.141086 284.46928,159.06916 Z"
|
||||
inkscape:transform-center-x="18.782775"
|
||||
inkscape:transform-center-y="1.4067283"
|
||||
transform="matrix(0.75380592,0,0,0.6968924,215.65938,154.56308)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:bevel;-inkscape-stroke:hairline;paint-order:fill markers stroke"
|
||||
id="path1577-3-56"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="143.94885"
|
||||
sodipodi:cy="162.33527"
|
||||
sodipodi:r1="281.11676"
|
||||
sodipodi:r2="140.55838"
|
||||
sodipodi:arg1="0.60507974"
|
||||
sodipodi:arg2="1.2333983"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 375.15523,322.24226 190.47831,294.96885 63.314968,431.63957 32.185209,247.57344 -137.092,168.86749 28.345668,82.381768 50.889936,-102.9319 184.2658,27.683114 367.47614,-8.141086 284.46928,159.06916 Z"
|
||||
inkscape:transform-center-x="18.782775"
|
||||
inkscape:transform-center-y="1.4067283"
|
||||
transform="matrix(0.75380592,0,0,0.6968924,2807.9794,657.35363)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:bevel;-inkscape-stroke:hairline;paint-order:fill markers stroke"
|
||||
id="path1577-3-7-2"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="143.94885"
|
||||
sodipodi:cy="162.33527"
|
||||
sodipodi:r1="281.11676"
|
||||
sodipodi:r2="140.55838"
|
||||
sodipodi:arg1="0.60507974"
|
||||
sodipodi:arg2="1.2333983"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 375.15523,322.24226 190.47831,294.96885 63.314968,431.63957 32.185209,247.57344 -137.092,168.86749 28.345668,82.381768 50.889936,-102.9319 184.2658,27.683114 367.47614,-8.141086 284.46928,159.06916 Z"
|
||||
inkscape:transform-center-x="18.782775"
|
||||
inkscape:transform-center-y="1.4067283"
|
||||
transform="matrix(0.75380592,0,0,0.6968924,2739.3997,137.32401)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:bevel;-inkscape-stroke:hairline;paint-order:fill markers stroke"
|
||||
id="path1577-3-5-9"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="143.94885"
|
||||
sodipodi:cy="162.33527"
|
||||
sodipodi:r1="281.11676"
|
||||
sodipodi:r2="140.55838"
|
||||
sodipodi:arg1="0.60507974"
|
||||
sodipodi:arg2="1.2333983"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 375.15523,322.24226 190.47831,294.96885 63.314968,431.63957 32.185209,247.57344 -137.092,168.86749 28.345668,82.381768 50.889936,-102.9319 184.2658,27.683114 367.47614,-8.141086 284.46928,159.06916 Z"
|
||||
inkscape:transform-center-x="18.782775"
|
||||
inkscape:transform-center-y="1.4067283"
|
||||
transform="matrix(0.75380592,0,0,0.6968924,2315.6258,664.22229)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:bevel;-inkscape-stroke:hairline;paint-order:fill markers stroke"
|
||||
id="path1577-3-7-3-1"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="143.94885"
|
||||
sodipodi:cy="162.33527"
|
||||
sodipodi:r1="281.11676"
|
||||
sodipodi:r2="140.55838"
|
||||
sodipodi:arg1="0.60507974"
|
||||
sodipodi:arg2="1.2333983"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 375.15523,322.24226 190.47831,294.96885 63.314968,431.63957 32.185209,247.57344 -137.092,168.86749 28.345668,82.381768 50.889936,-102.9319 184.2658,27.683114 367.47614,-8.141086 284.46928,159.06916 Z"
|
||||
inkscape:transform-center-x="18.782775"
|
||||
inkscape:transform-center-y="1.4067283"
|
||||
transform="matrix(0.75380592,0,0,0.6968924,2247.046,144.19267)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:bevel;-inkscape-stroke:hairline;paint-order:fill markers stroke"
|
||||
id="path1577-3-7-3-1-2"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="143.94885"
|
||||
sodipodi:cy="162.33527"
|
||||
sodipodi:r1="281.11676"
|
||||
sodipodi:r2="140.55838"
|
||||
sodipodi:arg1="0.60507974"
|
||||
sodipodi:arg2="1.2333983"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 375.15523,322.24226 190.47831,294.96885 63.314968,431.63957 32.185209,247.57344 -137.092,168.86749 28.345668,82.381768 50.889936,-102.9319 184.2658,27.683114 367.47614,-8.141086 284.46928,159.06916 Z"
|
||||
inkscape:transform-center-x="11.70076"
|
||||
inkscape:transform-center-y="0.76651195"
|
||||
transform="matrix(0.46958664,0,0,0.37972871,2079.8392,436.14535)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:bevel;-inkscape-stroke:hairline;paint-order:fill markers stroke"
|
||||
id="path1577-3-7-3-1-2-7"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="143.94885"
|
||||
sodipodi:cy="162.33527"
|
||||
sodipodi:r1="281.11676"
|
||||
sodipodi:r2="140.55838"
|
||||
sodipodi:arg1="0.60507974"
|
||||
sodipodi:arg2="1.2333983"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 375.15523,322.24226 190.47831,294.96885 63.314968,431.63957 32.185209,247.57344 -137.092,168.86749 28.345668,82.381768 50.889936,-102.9319 184.2658,27.683114 367.47614,-8.141086 284.46928,159.06916 Z"
|
||||
inkscape:transform-center-x="11.70076"
|
||||
inkscape:transform-center-y="0.76651195"
|
||||
transform="matrix(0.46958664,0,0,0.37972871,1822.022,810.93276)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:bevel;-inkscape-stroke:hairline;paint-order:fill markers stroke"
|
||||
id="path1577-3-7-3-1-2-7-0"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="143.94885"
|
||||
sodipodi:cy="162.33527"
|
||||
sodipodi:r1="281.11676"
|
||||
sodipodi:r2="140.55838"
|
||||
sodipodi:arg1="0.60507974"
|
||||
sodipodi:arg2="1.2333983"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 375.15523,322.24226 190.47831,294.96885 63.314968,431.63957 32.185209,247.57344 -137.092,168.86749 28.345668,82.381768 50.889936,-102.9319 184.2658,27.683114 367.47614,-8.141086 284.46928,159.06916 Z"
|
||||
inkscape:transform-center-x="11.70076"
|
||||
inkscape:transform-center-y="0.76651195"
|
||||
transform="matrix(0.46958664,0,0,0.37972871,2537.4397,462.2151)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:bevel;-inkscape-stroke:hairline;paint-order:fill markers stroke"
|
||||
id="path1577-3-7-3-1-2-7-9"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="143.94885"
|
||||
sodipodi:cy="162.33527"
|
||||
sodipodi:r1="281.11676"
|
||||
sodipodi:r2="140.55838"
|
||||
sodipodi:arg1="0.60507974"
|
||||
sodipodi:arg2="1.2333983"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 375.15523,322.24226 190.47831,294.96885 63.314968,431.63957 32.185209,247.57344 -137.092,168.86749 28.345668,82.381768 50.889936,-102.9319 184.2658,27.683114 367.47614,-8.141086 284.46928,159.06916 Z"
|
||||
inkscape:transform-center-x="11.70076"
|
||||
inkscape:transform-center-y="0.76651195"
|
||||
transform="matrix(0.46958664,0,0,0.37972871,1210.9897,703.77539)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:bevel;-inkscape-stroke:hairline;paint-order:fill markers stroke"
|
||||
id="path1577-3-7-3-1-2-7-3"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="143.94885"
|
||||
sodipodi:cy="162.33527"
|
||||
sodipodi:r1="281.11676"
|
||||
sodipodi:r2="140.55838"
|
||||
sodipodi:arg1="0.60507974"
|
||||
sodipodi:arg2="1.2333983"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 375.15523,322.24226 190.47831,294.96885 63.314968,431.63957 32.185209,247.57344 -137.092,168.86749 28.345668,82.381768 50.889936,-102.9319 184.2658,27.683114 367.47614,-8.141086 284.46928,159.06916 Z"
|
||||
inkscape:transform-center-x="11.70076"
|
||||
inkscape:transform-center-y="0.76651195"
|
||||
transform="matrix(0.46958664,0,0,0.37972871,1186.805,194.11559)" />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="vector-effect:non-scaling-stroke;fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:bevel;-inkscape-stroke:hairline;paint-order:fill markers stroke"
|
||||
id="path1577-3-7-3-1-2-7-6"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="143.94885"
|
||||
sodipodi:cy="162.33527"
|
||||
sodipodi:r1="281.11676"
|
||||
sodipodi:r2="140.55838"
|
||||
sodipodi:arg1="0.60507974"
|
||||
sodipodi:arg2="1.2333983"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 375.15523,322.24226 190.47831,294.96885 63.314968,431.63957 32.185209,247.57344 -137.092,168.86749 28.345668,82.381768 50.889936,-102.9319 184.2658,27.683114 367.47614,-8.141086 284.46928,159.06916 Z"
|
||||
inkscape:transform-center-x="11.70076"
|
||||
inkscape:transform-center-y="0.76651195"
|
||||
transform="matrix(0.46958664,0,0,0.37972871,558.24411,451.62824)" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 16 KiB |
23
tests/test_deploy_workflow.py
Normal file
23
tests/test_deploy_workflow.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import re
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class DeployWorkflowTests(unittest.TestCase):
|
||||
def test_deploy_workflow_triggers_on_master_pushes(self):
|
||||
workflow_path = Path(__file__).resolve().parents[1] / ".gitea" / "workflows" / "deploy-ftp.yml"
|
||||
workflow_text = workflow_path.read_text(encoding="utf-8")
|
||||
|
||||
branch_block_match = re.search(
|
||||
r"on:\s*\n\s*push:\s*\n\s*branches:\s*\n(?P<branches>(?:\s*-\s*[^\n]+\n)+)",
|
||||
workflow_text,
|
||||
)
|
||||
|
||||
self.assertIsNotNone(branch_block_match, "workflow should define push branches")
|
||||
branches = re.findall(r"-\s*([^\n]+)", branch_block_match.group("branches"))
|
||||
|
||||
self.assertEqual(branches, ["master"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
83
tests/test_package_firmware.py
Normal file
83
tests/test_package_firmware.py
Normal file
@@ -0,0 +1,83 @@
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from scripts.package_firmware import build_manifest, extract_firmware_version, package_bundle
|
||||
|
||||
|
||||
class PackageFirmwareTests(unittest.TestCase):
|
||||
def test_extract_firmware_version_reads_init_string(self):
|
||||
header_text = '#define initSting "EBBv13_and_above Protocol emulated by Eggduino-Firmware V1.6a"\n'
|
||||
|
||||
self.assertEqual(extract_firmware_version(header_text), "1.6a")
|
||||
|
||||
def test_build_manifest_matches_expected_esp32_offsets(self):
|
||||
manifest = build_manifest("1.6a")
|
||||
|
||||
self.assertEqual(manifest["name"], "EggDuino ESP32 Firmware")
|
||||
self.assertEqual(manifest["version"], "1.6a")
|
||||
self.assertTrue(manifest["new_install_prompt_erase"])
|
||||
self.assertEqual(manifest["builds"][0]["chipFamily"], "ESP32")
|
||||
self.assertEqual(
|
||||
manifest["builds"][0]["parts"],
|
||||
[
|
||||
{"path": "bootloader.bin", "offset": 4096},
|
||||
{"path": "partitions.bin", "offset": 32768},
|
||||
{"path": "boot_app0.bin", "offset": 57344},
|
||||
{"path": "firmware.bin", "offset": 65536},
|
||||
],
|
||||
)
|
||||
|
||||
def test_package_bundle_copies_outputs_and_removes_stale_files(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
temp_path = Path(temp_dir)
|
||||
build_dir = temp_path / "build"
|
||||
build_dir.mkdir()
|
||||
header_path = temp_path / "EggDuino.h"
|
||||
output_dir = temp_path / "firmware"
|
||||
output_dir.mkdir()
|
||||
boot_app0_path = temp_path / "boot_app0.bin"
|
||||
|
||||
build_artifacts = {
|
||||
"bootloader.bin": "bootloader",
|
||||
"partitions.bin": "partitions",
|
||||
"firmware.bin": "firmware-bin",
|
||||
"firmware.elf": "firmware-elf",
|
||||
"firmware.map": "firmware-map",
|
||||
}
|
||||
for file_name, contents in build_artifacts.items():
|
||||
(build_dir / file_name).write_text(contents, encoding="utf-8")
|
||||
|
||||
header_path.write_text(
|
||||
'#define initSting "EBBv13_and_above Protocol emulated by Eggduino-Firmware V1.6b"\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
boot_app0_path.write_text("boot-app0", encoding="utf-8")
|
||||
(output_dir / "stale.txt").write_text("old", encoding="utf-8")
|
||||
|
||||
package_bundle(
|
||||
build_dir=build_dir,
|
||||
boot_app0_path=boot_app0_path,
|
||||
header_path=header_path,
|
||||
output_dir=output_dir,
|
||||
)
|
||||
|
||||
self.assertFalse((output_dir / "stale.txt").exists())
|
||||
expected_files = {
|
||||
"bootloader.bin": "bootloader",
|
||||
"partitions.bin": "partitions",
|
||||
"firmware.bin": "firmware-bin",
|
||||
"firmware.elf": "firmware-elf",
|
||||
"firmware.map": "firmware-map",
|
||||
"boot_app0.bin": "boot-app0",
|
||||
}
|
||||
for file_name, contents in expected_files.items():
|
||||
self.assertEqual((output_dir / file_name).read_text(encoding="utf-8"), contents)
|
||||
|
||||
manifest = json.loads((output_dir / "manifest.json").read_text(encoding="utf-8"))
|
||||
self.assertEqual(manifest["version"], "1.6b")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user