Programming languages

Angular: How to inject child component class by ID via ViewChild

<app-my-component #myId></app-dynamic-plot>
@ViewChild('myid', {read: MyComponent}) myComponent!: MyComponent;

 

Posted by Uli Köhler in Angular

How to fix Hugo access denied: “asciidoctor” is not whitelisted in policy “security.exec.allow”; the current security configuration is:

Problem:

When compiling your Hugo page, you see an error message such as

ERROR render of "section" failed: "/home/uli/klc/themes/hugo-theme-techdoc/layouts/_default/list.html:3:4": execute of template failed: template: _default/list.html:3:4: executing "main" at <.Content>: error calling Content: "/home/uli/klc/content/footprint/F2/_index.adoc:1:1": access denied: "asciidoctor" is not whitelisted in policy "security.exec.allow"; the current security configuration is:

Solution:

You need to edit your Hugo configuration file and add asciidoctor or whatever program caused the error, to the list of allowed programs to execute.

For TOML config files, add the following section which consists of the default value plus asciidoctor

[security]
    [security.exec]
    allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^npx$', '^postcss$', '^asciidoctor$']

After that, recompile your Hugo page.

Posted by Uli Köhler in Python

How to fix Python cache3 ImportError: cannot import name ‘SafeCache’ from ‘cache3’

Problem:

You want to use cache3‘s SafeCache as shown in the Quickstart

from cache3 import SafeCache
cache = SafeCache()

but you instead see the following error message:

ImportError: cannot import name 'SafeCache' from 'cache3' (/usr/local/lib/python3.10/dist-packages/cache3/__init__.py)

Solution:

cache3 has been updated, but the documentation still has not been fixed. This is a known bug.

The closest equivalent is Cache() with thread_safe=True, an in-memory cache which supports tagging:

from cache3 import Cache
cache = Cache(name="mycache", thread_safe=True)

In case you don’t need tagging,  consider MiniCache:

from cache3 import MiniCache
cache = MiniCache(name="mycache", thread_safe=True)

 

Posted by Uli Köhler in Python

How to fix PySpice WARNING – Unsupported Ngspice version 36

Problem:

When you try to run your PySpice script, you see an error log such as

PySpice.Spice.NgSpice.Shared.NgSpiceShared._send_char - WARNING - spinit was not found
PySpice.Spice.NgSpice.Shared.NgSpiceShared._send_char - ERROR - Note: can't find init file.
PySpice.Spice.NgSpice.Shared.NgSpiceShared._init_ngspice - WARNING - Unsupported Ngspice version 36

Solution:

Your PySpice is too new for the ngspice version installed on your system.

Typically, it’s easiest to install a slightly older PySpice version:

sudo pip3 install -U "pyspice<1.5"

(but you can also update your ngspice).

Posted by Uli Köhler in Electronics, Python

How to install pip on Docker container without ensurepip

python3 -c "import urllib.request; urllib.request.urlretrieve('https://bootstrap.pypa.io/get-pip.py', 'get-pip.py')"
python3 get-pip.py --break-system-packages

 

Posted by Uli Köhler in Docker, Python

How to compute MOSFET gate charge loss power using Python

You can use the UliEngineering library to compute the power lost to charging a particular MOSFET to a particular gate voltage a given number of times a second:

from UliEngineering.Electronics.MOSFET import mosfet_gate_charge_losses
from UliEngineering.EngineerIO import auto_format

auto_format(mosfet_gate_charge_losses, "31nC", "9V", "1MHz")
# Prints '279 mW'

 

Posted by Uli Köhler in Python

CadQuery example: Latching pin

import cadquery as cq
import math

# Set parameters
n = 2 # Number of pins
P = 3.96 # mm pin pitch
C1 = 6.8 # mm Height of bottom plate
H1 = 9.4 # mm Height of latching plate
O1 = 2 # mm Vertical offset pin-center to bottom plate border
D1 = 3.2 # mm Total depth of bottom plate
Pw = 1.14 # mm Width & height of square pin
D2 = 1.3 # mm (Measured) depth of latching plate (narrowest section)
Wx1 = 1.0 # mm (Measured) Width of the topmost part of the latching plate
Hx1 = 0.5 # mm (Measured) Height of the latching surface facing away from the pins
alpha = 90 # ° Angle of the latching surface. Shown by the datasheet as ~45°but real parts have 90°
beta = 15 # ° (Estimated) Latching plate tapering angle
WL1 = 1.27 # mm (Measured) With of the section of the bottom plate where there is no latching plate per side)
Dlatch = 1.75 # mm (Measured) Total depth of latching plate
delta = 70 # ° (Estimated) Taper angle of the pin top & bottoms
FilletRadiusBaseplate = 0.4 # mm

# Compute derived variables
A1 = ( n - 1 ) * P # Total width of all pins (center of last pin distance to center of first pin)
B1 = A1 + ( 1.95 * 2 ) # Width of bottom plate
LLatch = B1 - ( WL1 * 2 ) # Length of the latching plate
LPinTop = 7.7 + D1 # Length of the pin measured from the bottom plane to the top of the pin (away from PCB)
LPinBottom = 14.6 - LPinTop # Length of the pin in bottom direction, measured from the bottom plane
Pin1XOffset = ( ( n - 1 ) * P ) / 2 # First pin X offset measured from the X axis

# Compute asymmetric chamfer with constant angle triangle lengths
# Note: Top-facing angle of the chamfering triangle is [beta]
opposing_side = (Dlatch - Wx1) / 2 # Compute so that remaining part of latch is Wx1 wide
adjacent_side = opposing_side * (1/math.tan(math.radians(beta)))

# Start just outside the bottom plate
xstart = C1/2
xend = xstart + Dlatch
h1MinusChamfer = H1 - adjacent_side
latchPlate = (cq.Workplane("YZ")
    .sketch()
    # From bottom to start of chamfer
    .segment((xstart, 0), (xstart, h1MinusChamfer))
    # Chamfer edge
    .segment((xstart + opposing_side, H1))
    # Top side of latch (straight line) (up to begin of other side chamfer)
    .segment((xend - opposing_side, H1))
    # Chamfer edge
    .segment((xend, h1MinusChamfer))
    # move down to begin of latching surface
    .segment((xend, h1MinusChamfer - Hx1))
    # Move inwards (-X) for latching surface. NOTE: we assume 90° angle here
    # NOTE: D2 is the depth of the latching plate below the latching surface
    .segment((xstart + D2, h1MinusChamfer - Hx1))
    # From latching surface inside to bottom
    .segment((xstart + D2, 0))
    .close()
    .assemble(tag="face")
    .finalize()
    .extrude(1.0)
)
latchPlate

 

Posted by Uli Köhler in CadQuery, Python

CadQuery: How to fix cq.Location() error: TypeError: Expected three floats, OCC gp_, or 3-tuple

Problem

You are trying to construct a cq.Location object using code like

cq.Location(1, 2, 3)

however when you run it, you see the following error message:

File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\cadquery\occ_impl\geom.py:1011, in Location.__init__(self, *args)
   1008 else:
   1009     t, ax, angle = args
   1010     T.SetRotation(
-> 1011         gp_Ax1(Vector().toPnt(), Vector(ax).toDir()), angle * math.pi / 180.0
   1012     )
   1013     T.SetTranslationPart(Vector(t).wrapped)
   1015 self.wrapped = TopLoc_Location(T)

File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\cadquery\occ_impl\geom.py:91, in Vector.__init__(self, *args)
     89         fV = gp_Vec(args[0])
     90     else:
---> 91         raise TypeError("Expected three floats, OCC gp_, or 3-tuple")
     92 elif len(args) == 0:
     93     fV = gp_Vec(0, 0, 0)

TypeError: Expected three floats, OCC gp_, or 3-tuple

Solution:

Don’t pass three separate parameters to cq.Location() – pass a tuple with three numbers by adding an additional set of braces:

cq.Location((1, 2, 3))

 

Posted by Uli Köhler in CadQuery, Python

CadQuery: Asymmetric chamfer minimal example

import cadquery as cq

obj = cq.Workplane("XY").box(1,1,3)
# Add chamfer to only the top face edges parallel to the X axis
obj = obj.faces("+Z").edges("|X").chamfer(0.8, 0.3)
obj

Posted by Uli Köhler in CadQuery, Python

CadQuery: How to chamfer only two opposing edges of top face

import cadquery as cq

obj = cq.Workplane("XY").box(1,1,3)
# Add chamfer to only the top face edges parallel to the X axis
obj = obj.faces("+Z").edges("|X").chamfer(0.2)
obj

Posted by Uli Köhler in CadQuery, Python

CadQuery: How to chamfer top face of object

import cadquery as cq

obj = cq.Workplane("XY").box(1,1,3)
# Add chamfer to all top face edges
obj = obj.faces("+Z").chamfer(0.2)
obj

Posted by Uli Köhler in CadQuery, Python

CadQuery: Chamfer in sketch minimal example

This code chamfers the top side of a sketch rectangle, which is then extruded

import cadquery as cq

obj = (cq.Workplane("YZ")
    .sketch()
    .rect(1,4)
    # Select vertices to chamfer
    .vertices(">Y") # Top of local Y coordinate system (which is Z axis)
    .chamfer(0.2)
    .finalize()
    .extrude(0.1)
)
obj

Posted by Uli Köhler in CadQuery, Python

CadQuery minimal sketch from segment() lines example

This code creates a sketch from four segment lines forming a rectangle (given the four sets of X/Y coordinates). The segments are then assembled into a face and extruded.

import cadquery as cq

xstart = 1.0
height = 4.0
width = 1.0
obj = (cq.Workplane("YZ")
    .sketch()
    .segment((xstart, 0), (xstart, height))
    .segment((xstart + width, height))
    .segment((xstart + width, 0))
    .close()
    .assemble(tag="face")
    .finalize()
    .extrude(0.1)
)
obj

Posted by Uli Köhler in CadQuery, Python

CadQuery sketch: How to move to different location

In order to move to a certain position in a CadQuery sketch, use .push(cq.Location(...))

Full example

import cadquery as cq

# Create workplane (2d coordinate system for us to create the sketch in)
wp = cq.Workplane("XY")
# Create sketch
result = wp.sketch().push(cq.Location((1,0.5,0))).rect(1,1).finalize().extrude(0.1)

result # This line is just to show the result in cq-editor or jupyter

Posted by Uli Köhler in CadQuery, Python

CadQuery minimal rectangular array (rarray) example

This example generates a 2x2 grid of 1x1mm rectangles in a sketch, then extrudes it

import cadquery as cq

# Create workplane (2d coordinate system for us to create the sketch in)
wp = cq.Workplane("XY")
# Create sketch & extrude
result = wp.sketch().rarray(
    1.5, # X distance between center points of rectangles
    1.5, # Y distance between center points of rectangles
    2, # Number of rectangles in X direction
    2 # Number of rectangles in Y direction
).rect(1,1).finalize().extrude(0.1)

result # This line is just to show the result in cq-editor or jupyter

Posted by Uli Köhler in CadQuery, Python

CadQuery: How to move/translate extruded sketch

In our previous example CadQuery minimal sketch extrude example we showed how to create and extrude a simple sketch.

You can translate this easily using

result = result.translate(cq.Vector(1,0,0))

Full example

import cadquery as cq

# Create workplane (2d coordinate system for us to create the sketch in)
wp = cq.Workplane("XY")
# Create sketch, create rect, close sketch and extrude the resulting face
result = wp.sketch().rect(2, 2).finalize().extrude(0.1)
result = result.translate(cq.Vector(1,0,0))

result # This line is just to show the result in cq-editor or jupyter

Posted by Uli Köhler in CadQuery, Python

CadQuery minimal sketch extrude example

The folllowing example creates a sketch in the XY plane, creates a 2x2mm rectangle in said sketch and extrudes it to a height of 0.1mm.

import cadquery as cq

# Create workplane (2d coordinate system for us to create the sketch in)
wp = cq.Workplane("XY")
# Create sketch, create rect, close sketch and extrude the resulting face
result = wp.sketch().rect(2, 2).finalize().extrude(0.1)

result # This line is just to show the result in cq-editor or jupyter

Posted by Uli Köhler in CadQuery, Python

CadQuery simple L-shaped piece example (with STEP export)

import cadquery as cq

# Define dimensions
base_length = 200  # 20cm
base_width = 80    # 8cm
plate_thickness = 2  # 2mm
upright_height = 400  # 40cm

# Create the base plate
base_plate = cq.Workplane("XY").box(base_length, base_width, plate_thickness)

# Create the upright plate
# Position is set such that it aligns with the end of the base plate and stands upright
upright_plate = cq.Workplane("XY").workplane(offset=plate_thickness).transformed(rotate=(0, 90, 0)).box(upright_height, base_width, plate_thickness).translate((base_length/2, 0, upright_height/2))

# Join the two parts into one
final_part = base_plate.union(upright_plate)

# Export to STEP
final_part.val().exportStep("L-piece.stp")

 

Posted by Uli Köhler in CadQuery, Python

Jupyter adjustment widgets with plus and minus buttons

First, define get and set functions:

# Basic examples for get and set value functions

def get_value(): # will only be used to get the initial value
    return httpx.get(f"http://{ip}/api/get-value").json()["value"]

def set_value(value):
    httpx.get(f"http://{ip}/api/set-value?nedge={value}")

 

import ipywidgets as widgets
from IPython.display import display

# Step 2: Define the widget components
value_display = widgets.IntText(value=get_value(), description='Value:', disabled=False)
plus_button = widgets.Button(description='+')
minus_button = widgets.Button(description='-')

def on_value_change(change):
    set_value(change['new'])
    
value_display.observe(on_value_change, names='value')

# Step 4: Define the update functions
def on_plus_button_clicked(b):
    value_display.value += 1

def on_minus_button_clicked(b):
    value_display.value -= 1

# Step 5: Bind the update functions to the buttons
plus_button.on_click(on_plus_button_clicked)
minus_button.on_click(on_minus_button_clicked)

# Step 6: Display the widgets
widgets_layout = widgets
display(value_display, plus_button, minus_button)

 

Posted by Uli Köhler in Jupyter, Python

STM32H743 DAC instant switch off (no fall time)

In our previous post STM32H743 DAC rise/fall time experiments we showed that the STM23H743 has relatively long turn-on / turn-off times of approximately 950 nanoseconds, limiting the generation of fast rectangular signals:

However, there’s a trick how to obtain fall times almost 3 orders of magnitude better for the special case of switching either to full-scale VDD or to GND.

Instead of setting the DAC to the new value, you can just disable the DAC and let the GPIO take care of the rest. This allows for extremely fast fall times of approximately 5ns.

Note that switching the GPIO to digital mode while the DAC is on does not seem to have any effect.

Please note that I didn’t take any care to make the measurement setup immune to the high transients, leading to some oscillation. You should take your own measurements if you have specific requirements.

DAC fall time from 3/4 full scale value (3072)

Note that switching on the DAC quickly is outside the scope of this post, but when toggling the DAC enable bit, you can still obtain 250ns rise times pretty easily.

Code example

void Init() {    
    DAC_ChannelConfTypeDef sConfig = {0};

    // Initialize DAC
    hdac.Instance = DAC1;
    if (HAL_DAC_Init(&hdac) != HAL_OK)
    {
        // Initialization Error
        __BKPT();
    }

    // Configure DAC channel
    sConfig.DAC_Trigger = DAC_TRIGGER_NONE;  // No trigger, free-running mode
    sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;

    if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK)
    {
        // Channel configuration Error
        __BKPT();
    }

    // Enable DAC Channel and start the conversion
    if (HAL_DAC_Start(&hdac, DAC_CHANNEL_1) != HAL_OK)
    {
        // Starting Error
        __BKPT();
    }

    // Set DAC to some value, which won't be changed for this example
    if (HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 3072) != HAL_OK)
    {
        // Setting DAC value Error
         __BKPT();
    }
}
void Pulse_On() {
    // Enable DAC
    hdac.Instance->CR |= DAC_CR_EN1;

    // Set GPIO to analog mode
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

void Pulse_Off() {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);

    // Clear EN1 bit of DAC_CR
    hdac.Instance->CR &= ~DAC_CR_EN1;
}

 

 

Posted by Uli Köhler in C/C++, STM32