Fix extension exporting bug
@ -18,3 +18,4 @@ Click on a link in the list below to go to that page:
|
||||
1. [Omnibus F4 V3 pinout](../../drone-stuff/omnibus-f4-v3-pinout)
|
||||
1. [Omnibus F4 pro servo diode](../../drone-stuff/omnibus-f4-pro-servo-diode)
|
||||
1. [Transmitter external module pinout](../../drone-stuff/transmitter-external-module-pinout)
|
||||
1. [Transportable C1 Chaser](../../drone-stuff/transportable-c1-chaser)
|
||||
|
@ -7,11 +7,11 @@ insert_anchor_links = "right"
|
||||
To get uninverted SBUS/SmartPort on the FrSky XSR/X4RS receiver, you can repurpose the CPPM pad.
|
||||
Remove the two small resistors shown in the image, and solder the two lower pads (together) to either the CPPM pad or the MOSFET pin shown in the photo:
|
||||
|
||||
[![xsr-sbus.jpeg](../../resources/f86da9a7aac1413ebd77825897164f7f.jpeg)](../../resources/f86da9a7aac1413ebd77825897164f7f.jpeg)
|
||||
[![xsr-sbus.jpeg](../../resources/f86da9a7aac1413ebd77825897164f7f.jpg)](../../resources/f86da9a7aac1413ebd77825897164f7f.jpg)
|
||||
|
||||
They should be soldered like this (remember to solder both resistor pads together):
|
||||
|
||||
[![xsr-sbus2.jpeg](../../resources/815576429ece43789dbc70dfd33517a1.jpeg)](../../resources/815576429ece43789dbc70dfd33517a1.jpeg)
|
||||
[![xsr-sbus2.jpeg](../../resources/815576429ece43789dbc70dfd33517a1.jpg)](../../resources/815576429ece43789dbc70dfd33517a1.jpg)
|
||||
|
||||
Now the CPPM pad will be uninverted SBUS/SmartPort instead.
|
||||
It seems to be a bit of a gamble whether you get SBUS or SmartPort, it might be firmware-dependent.
|
||||
|
@ -14,7 +14,7 @@ The transmitter (Taranis, Jumper, RadioMaster, etc) pinout is, from top to botto
|
||||
|
||||
It's illustrated in this photo:
|
||||
|
||||
[![pinout.jpeg](../../resources/72d23239af1541d4b170271c1e9e21eb.jpeg)](../../resources/72d23239af1541d4b170271c1e9e21eb.jpeg)
|
||||
[![pinout.jpeg](../../resources/72d23239af1541d4b170271c1e9e21eb.jpg)](../../resources/72d23239af1541d4b170271c1e9e21eb.jpg)
|
||||
|
||||
|
||||
|
||||
|
45
content/drone-stuff/transportable-c1-chaser.md
Normal file
@ -0,0 +1,45 @@
|
||||
+++
|
||||
title = "Transportable C1 Chaser"
|
||||
weight = 11
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
I have a [C1 Chaser](https://www.banggood.com/C1-Chaser-1200mm-Wingspan-EPO-Flying-Wing-FPV-Racer-Aircraft-RC-Airplane-KIT-p-1102080.html?custlinkid=667416&p=6W16207412782201611V), and it's a fantastic wing.
|
||||
It flies great, and is very efficient. The only problem I had with it was that it's too long to easily carry around, as it has a 1.2m wingspan.
|
||||
|
||||
I thought that a C1 Chaser that can break apart for easy transport would be the ideal long-range wing, so I bought a second one and made some modifications to it.
|
||||
I'm listing the modifications here so you can do the same if you want to.
|
||||
|
||||
|
||||
## Spar
|
||||
|
||||
The biggest issue is making the spar removable.
|
||||
The best way I've found to do that is to use IKEA drinking straws, they have an internal diameter of around 7.5mm, making them ideal for putting the spar through.
|
||||
|
||||
I've cut three straws to length and glued them in the spar channel, as you can see here:
|
||||
|
||||
[![](../../resources/5ac6bff04962497083b8403ed0fded98.jpg)](../../resources/5ac6bff04962497083b8403ed0fded98.jpg)
|
||||
|
||||
|
||||
[![](../../resources/65ff483180ad483795b32d0b65f72613.jpg)](../../resources/65ff483180ad483795b32d0b65f72613.jpg)
|
||||
|
||||
For glue, I use [E6000](https://www.banggood.com/ZHANLIDA-152550ml-E6000-B6000-Adhesive-Transparent-Glue-for-RC-Models-p-1604315.html?cur_warehouse=CN&ID=62834216283418&rmmds=search&custlinkid=667416&p=6W16207412782201611V) (sold as Goop in the US) for pretty much everything, but for this one you can use your favorite glue that you know won't dissolve EPO.
|
||||
|
||||
|
||||
## Wings
|
||||
|
||||
TODO: I haven't really gotten there yet.
|
||||
|
||||
|
||||
## Vertical stabilizers
|
||||
|
||||
The last part is securing the vertical stabilizers. I did this with two very small 3D printed pieces, I slot the stabilizers horizontally (on the left/right axis) onto the fuselage and then connect the wings, which keeps the stabilizers securely in place.
|
||||
|
||||
TODO: I'll add more photos here when this is glued and finalized.
|
||||
|
||||
* * *
|
||||
|
||||
<p style="font-size:80%; font-style: italic">
|
||||
Last updated on January 31, 2021. For any questions/feedback,
|
||||
email me at <a href="mailto:hi@stavros.io">hi@stavros.io</a>.
|
||||
</p>
|
@ -6,7 +6,7 @@ insert_anchor_links = "right"
|
||||
+++
|
||||
These notes are a condensed version of the [FT Arrow build video](https://www.youtube.com/watch?v=cD2Ca2oskVs).
|
||||
|
||||
![maxresdefault.jpeg](../../resources/d4819ca1c3d0490daaa12f62af09aa00.jpeg)
|
||||
![maxresdefault.jpeg](../../resources/d4819ca1c3d0490daaa12f62af09aa00.jpg)
|
||||
|
||||
|
||||
## Wings
|
||||
|
@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
import dataclasses
|
||||
import mimetypes
|
||||
import re
|
||||
import sqlite3
|
||||
from collections import defaultdict
|
||||
@ -6,7 +8,10 @@ from datetime import datetime
|
||||
from pathlib import Path
|
||||
from shutil import copy
|
||||
from shutil import rmtree
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Set
|
||||
|
||||
|
||||
def contains_word(word: str, text: str) -> bool:
|
||||
@ -23,32 +28,39 @@ def slugify(text):
|
||||
return re.sub(r"[\W_]+", "-", text.lower()).strip("-")
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Note:
|
||||
"""A helper type for a note."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id,
|
||||
parent_id,
|
||||
parent_title,
|
||||
title,
|
||||
body,
|
||||
updated_time,
|
||||
tags=[],
|
||||
):
|
||||
self.id = id
|
||||
self.parent_id = parent_id
|
||||
self.parent_title = parent_title
|
||||
self.title = title
|
||||
self.body = body
|
||||
self.updated_time = datetime.fromtimestamp(updated_time)
|
||||
self.tags = tags
|
||||
id: str
|
||||
parent_id: str
|
||||
parent_title: str
|
||||
title: str
|
||||
body: str
|
||||
updated_time: datetime
|
||||
tags: List[str] = dataclasses.field(default_factory=list)
|
||||
|
||||
def get_url(self):
|
||||
"""Return the note's relative URL."""
|
||||
return slugify(self.parent_title) + "/" + slugify(self.title)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Resource:
|
||||
"""A helper type for a resource."""
|
||||
|
||||
title: str
|
||||
# The actual extension that the file stored in Joplin has.
|
||||
extension: str
|
||||
mimetype: str
|
||||
|
||||
@property
|
||||
def derived_ext(self):
|
||||
"""Return an extension derived from the resource's mime type."""
|
||||
ext = mimetypes.guess_extension(self.mimetype, strict=False)
|
||||
return "" if ext is None else ext
|
||||
|
||||
|
||||
class JoplinExporter:
|
||||
"""The main exporter class."""
|
||||
|
||||
@ -57,9 +69,8 @@ class JoplinExporter:
|
||||
joplin_dir = Path.home() / ".config/joplin-desktop"
|
||||
|
||||
def __init__(self):
|
||||
# A dict of {resource_id: (title, extension)}.
|
||||
self.resources = {}
|
||||
self.used_resources = set()
|
||||
self.resources: Dict[str, Resource] = {}
|
||||
self.used_resources: Set[str] = set()
|
||||
|
||||
def clean_content_dir(self):
|
||||
"""Reset the content directory to a known state to begin."""
|
||||
@ -99,16 +110,15 @@ class JoplinExporter:
|
||||
# Add the resource to the set of used resources, so we can only copy
|
||||
# the resources that are used.
|
||||
self.used_resources.add(resource_id)
|
||||
return "resources/" + resource_id + "." + resource[1]
|
||||
return "resources/" + resource_id + resource.derived_ext
|
||||
|
||||
def copy_resources(self):
|
||||
"""Copy all the used resources to the output directory."""
|
||||
for resource_id in self.used_resources:
|
||||
resource = self.resources[resource_id]
|
||||
title, extension = resource
|
||||
copy(
|
||||
self.joplin_dir / "resources" / (f"{resource_id}.{extension}"),
|
||||
self.static_dir,
|
||||
self.joplin_dir / "resources" / (f"{resource_id}.{resource.extension}"),
|
||||
self.static_dir / f"{resource_id}{resource.derived_ext}",
|
||||
)
|
||||
|
||||
def read_data(self):
|
||||
@ -128,8 +138,16 @@ class JoplinExporter:
|
||||
for note_id, tag_id in c.fetchall():
|
||||
note_tags[note_id].append(tags[tag_id])
|
||||
|
||||
c.execute("""SELECT id, title, file_extension FROM resources;""")
|
||||
self.resources = {id: (title, ext) for id, title, ext in c.fetchall()}
|
||||
c.execute("""SELECT id, title, mime, file_extension FROM resources;""")
|
||||
|
||||
self.resources = {
|
||||
id: Resource(
|
||||
title=title,
|
||||
extension=ext,
|
||||
mimetype=mime,
|
||||
)
|
||||
for id, title, mime, ext in c.fetchall()
|
||||
}
|
||||
|
||||
c.execute("""SELECT id, parent_id, title, body, updated_time FROM notes;""")
|
||||
self.notes = defaultdict(list)
|
||||
@ -141,7 +159,7 @@ class JoplinExporter:
|
||||
self.folders[parent_id],
|
||||
title,
|
||||
body,
|
||||
updated_time / 1000,
|
||||
datetime.fromtimestamp(updated_time / 1000),
|
||||
tags=note_tags[id],
|
||||
)
|
||||
self.notes[note.parent_id].append(note)
|
||||
|
BIN
static/resources/5ac6bff04962497083b8403ed0fded98.jpg
Normal file
After Width: | Height: | Size: 237 KiB |
BIN
static/resources/65ff483180ad483795b32d0b65f72613.jpg
Normal file
After Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |