What is the MRI excitation frequency for 1.5T and 3T MRI scanners?

The base excitation frequency (for hydrogen) is:

  • 63.87 MHz for 1.5 Tesla MRI scanners
  • 127.74 MHz for 3 Tesla MRI scanners

Note that the excitation frequency is not exactly one frequency but varies spatially based on the gradient field strength.

You can calculate the excitation frequency using

Excitation frequency [MHz] = 42.58 MHz/T * [magnetic field strength in Tesla]

For more info, see mriquestions.com

Posted by Uli Köhler in Electronics, Medical devices

Which pins on the ESP32-S2 are DAC outputs?

Only two pins on the ESP32-S2 are capable of being DAC outputs:

  • GPIO23 is DAC_1
  • GPIO24 is DAC_2

Source: ESP32-S2 datasheet

Posted by Uli Köhler in ESP8266/ESP32

How to output 2.048MHz clock on any pin on the ESP32

You can use the LEDC timer (typically used for PWM) to output a 50% duty cycle clock with 3.3V P-P amplitude on any output-capable GPIO pin.

First,

#include <driver/ledc.h>

then setup the timer. You only need to do this once on startup, no code in your loop function is required.

/**
 * Setup 2.048MHz clock output on GPIO33
 */
ledc_timer_config_t ledc_timer = {
    .speed_mode = LEDC_HIGH_SPEED_MODE,
    .bit_num    = LEDC_TIMER_2_BIT,
    .timer_num  = LEDC_TIMER_0,
    .freq_hz    = 2048000
};
ledc_channel_config_t ledc_channel = {
    .gpio_num   = GPIO_NUM_33,
    .speed_mode = LEDC_HIGH_SPEED_MODE,
    .channel    = LEDC_CHANNEL_0,
    .timer_sel  = LEDC_TIMER_0,
    .duty       = 2
};
ledc_timer_config(&ledc_timer);
ledc_channel_config(&ledc_channel);

 

Posted by Uli Köhler in ESP8266/ESP32

How does the ESP32 DAC cosine generator waveform look on an Oscilloscope?

The ESP32 DAC has a built-in cosine waveform generator. Even though it’s an 8-bit DAC, the waveform looks pretty clean.

For an example on how to generate this wavefrm in firmware, see How to use the ESP32 DAC sine/cosine waveform generator using Arduino / PlatformIO

Posted by Uli Köhler in Electronics, ESP8266/ESP32

How to use the ESP32 DAC sine/cosine waveform generator using Arduino / PlatformIO

The ESP32 and its derivatives such as the ESP32-S2 have a built-in sine/cosine waveform generator for the built-in 8-bit DAC.

Using it requires ESP-IDF v5.1+ (see the official example). Using it with Arduino is slightly harder, since the stable version of the arduino-esp32 framework at the time of writing this post is based on ESP-IDF v4.4 which does not provide the DAC cosine generator API.

Therefore, we have to explicitly specify the arduino-espressif32 version (git commit) in platformio.ini:

[env:esp32dev]
platform = espressif32
# Commit f9cddfde697b659b9e818ec514f1505d2bd4a8ae is branch esp-idf-v5.1-libs @2022-02-01
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#f9cddfde697b659b9e818ec514f1505d2bd4a8ae
board = esp32dev
framework = arduino

The example main source code is pretty simple:

#include <Arduino.h>
#include <driver/dac_cosine.h>

void setup() {
    dac_cosine_handle_t chan0_handle;
    dac_cosine_config_t cos0_cfg = {
        .chan_id = DAC_CHAN_1, // GPIO26
        .freq_hz = 1000,
        .clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
        .atten = DAC_COSINE_ATTEN_DEFAULT,  
        .phase = DAC_COSINE_PHASE_0,  
        .offset = 0,
        //.flags.force_set_freq = false,
    };
    ESP_ERROR_CHECK(dac_cosine_new_channel(&cos0_cfg, &chan0_handle));
    ESP_ERROR_CHECK(dac_cosine_start(chan0_handle));
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(1000);
}

If you want to see how the generated waveform looks on an oscilloscope, see How does the ESP32 DAC cosine generator waveform look on an Oscilloscope?

Posted by Uli Köhler in Arduino, ESP8266/ESP32, PlatformIO

What is the the IMPD bit for the MAX11200EEE+ / MAX112100EEE+?

On the MAX11200EEE+ and the MAX112100EEE+, bit 3 of the command byte is named IMPD but this bit is never described in detail in the datasheet.

The meaning of this bit is Immediate power-down. When you set it to 1, the IC will switch to power-down mode. This is evidenced by the power-down example in Table 8 in the datasheet.

Posted by Uli Köhler in Components, Electronics

What is the correct SPI mode for MAX11200EEE+ / MAX112100EEE+?

The MAX11200EEE+ and the MAX112100EEE+ operate using SPI Mode 0Therefore, CPHA=0 & CPOL=0.

For more information, see the datasheet.

Posted by Uli Köhler in Components, Electronics

Minimal PlatformIO ESP32 ArduinoOTA example

Based on our Minimal PlatformIO ESP8266 ArduinoOTA example, this is a minimal starting point for your ESP32 program running ArduinoOTA.

#include <Arduino.h>
#include <WiFi.h>
#include <ArduinoOTA.h>


void setup() {
  Serial.begin(115200);
  /**
   * Connect to Wifi
   */
  WiFi.begin("MyWifi", "abc123abc");
  uint32_t notConnectedCounter = 0;
  while (WiFi.status() != WL_CONNECTED) {
      delay(100);
      Serial.println("Wifi connecting...");
      notConnectedCounter++;
      if(notConnectedCounter > 150) { // Reset board if not connected after 15s
          Serial.println("Resetting due to Wifi not connecting...");
          ESP.restart();
      }
  }
  Serial.print("Wifi connected, IP address: ");
  Serial.println(WiFi.localIP());
  /**
   * Enable OTA update
   */
  ArduinoOTA.begin();
}

void loop() {
  // Check for over the air update request and (if present) flash it
  ArduinoOTA.handle();
}

platformio.ini

You can leave your platformio.ini at default values, the only aspect you need to change is to set monitor_speed = 115200

[env:esp32dev]
board = esp32dev
platform = espressif32
framework = arduino
monitor_speed = 115200
Posted by Uli Köhler in ESP8266/ESP32, PlatformIO

How to parse filename extension from URL using Python

If you have an URL like

https://logodix.com/logo/1667872.jpg?param=value

and you want to find just the filename extension (.jpg for this example), use the following code:

from urllib.parse import urlsplit
import os.path

url = "https://logodix.com/logo/1667872.jpg?param=value"

path = urlsplit(url).path
extension = os.path.splitext(path)[-1] # e.g. ".jpg"

 

Posted by Uli Köhler in Python

How to fix Python 3 AttributeError: ‘function’ object has no attribute ‘urlsplit’

Problem:

When trying to urlsplit an URL in Python 3:

from urllib.parse import urlparse

path = urlparse.urlsplit(remote_image_url).path

you see the following error message:

AttributeError                            Traceback (most recent call last)
Input In [30], in <cell line: 3>()
      1 from urllib.parse import urlparse
----> 3 path = urlparse.urlsplit(remote_image_url).path
      4 filename = posixpath.basename(path)

AttributeError: 'function' object has no attribute 'urlsplit'

Solution:

The equivalent of urlparse.urlsplit() in Python 3 is urllib.parse.urlsplit().

Therefore, a working code example is

from urllib.parse import urlsplit

path = urlsplit(remote_image_url).path

 

Posted by Uli Köhler in Python

How to fix Python 3 ModuleNotFoundError: No module named ‘urlparse’

Problem:

While trying to import urlparse in Python 3 using

import urlparse

you see the following error message:

---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Input In [29], in <cell line: 1>()
----> 1 import urlparse

ModuleNotFoundError: No module named 'urlparse'

Solution:

In Python 3, urlparse has been renamed to urllib.urlparse. In order to import it, use this line instead:

from urllib.parse import urlparse

 

Posted by Uli Köhler in Python

How to fix InvenTree remote_image: Downloading images from remote URL is not enabled

Problem:

While trying to create an InvenTree object using a remote_image URL, you see an error message like

requests.exceptions.HTTPError: {'detail': 'Error occurred during API request', 'url': 'https://inventree-test.mydomain.com/api/company/', 'method': 'POST', 'status_code': 400, 'body': '{"remote_image":["Downloading images from remote URL is not enabled"]}', 'headers': {'AUTHORIZATION': 'Token 6daae3817756e9c1a3603b14d9582e61f50db388'}, 'params': {'format': 'json'}, 'data': {'name': 'DigiKey', 'website': 'https://www.digikey.de/', 'remote_image': 'https://logodix.com/logo/1667872.jpg', 'description': '', 'is_manufacturer': False, 'is_supplier': True, 'is_customer': False, 'currency': 'EUR'}}

Solution:

Open the InvenTree webinterface, open Server Configuration on the left

and enable Download from URL.

The setting will be effective immediately. There is no need to click any Apply button or restart the server.

 

Posted by Uli Köhler in Allgemein

InvenTree Traefik reverse proxy container labels

This post is based on How to install InvenTree using docker in just 5 minutes and uses the auto-generated docker-compose.yml from there. However it should be useable for almost any setup.

The setup is pretty standard since the inventree proxy container runs the webserver on port 80. Therefore, you don’t even have to explicitly specify a load balancer port

In your docker-compose.yml, add the following labels to the inventree-proxy container:

Wildcard certificate setup:

For more details on the base Traefik setup, see Simple Traefik docker-compose setup with Lets Encrypt Cloudflare DNS-01 & TLS-ALPN-01 & HTTP-01 challenges

labels:
    - "traefik.enable=true"
    - "traefik.http.routers.inventree-mydomain.rule=Host(`inventree.mydomain.com`)"
    - "traefik.http.routers.inventree-mydomain.entrypoints=websecure"
    - "traefik.http.routers.inventree-mydomain.tls.certresolver=cloudflare"
    - "traefik.http.routers.inventree-mydomain.tls.domains[0].main=mydomain.com"
    - "traefik.http.routers.inventree-mydomain.tls.domains[0].sans=*.mydomain.com"

 

Posted by Uli Köhler in InvenTree, Traefik

How to install InvenTree using docker in just 5 minutes

The following script is an automated installation script for InvenTree that fetches the current docker-compose.yml and other configs from GitHub, modifies them so that only local directories are used for storage and then setups InvenTree.

First, create a directory such as /opt/inventree-mydomain. I recommend to choose a unique directory name and not just inventree to keep instae

 

#!/bin/sh
wget -O nginx.prod.conf https://github.com/inventree/InvenTree/raw/master/docker/production/nginx.prod.conf
wget -O docker-compose.yml https://github.com/inventree/InvenTree/raw/master/docker/production/docker-compose.yml
wget -O .env https://github.com/inventree/InvenTree/raw/master/docker/production/.env

sed -i -e 's/#INVENTREE_DB_USER=pguser/INVENTREE_DB_USER=inventree/g' .env
sed -i -e "s/#INVENTREE_DB_PASSWORD=pgpassword/INVENTREE_DB_PASSWORD=$(pwgen 30 1)/g" .env
sed -i -e "s/INVENTREE_WEB_PORT=1337/INVENTREE_WEB_PORT=$(shuf -i 1024-65535 -n 1)/g" .env
sed -i -e "s/#INVENTREE_ADMIN_USER=/INVENTREE_ADMIN_USER=admin/g" .env
sed -i -e "s/#INVENTREE_ADMIN_PASSWORD=/INVENTREE_ADMIN_PASSWORD=$(pwgen 30 1)/g" .env
sed -i -e "s/#INVENTREE_ADMIN_EMAIL=/[email protected]/g" .env
sed -i -e 's/COMPOSE_PROJECT_NAME=inventree-production//g' .env
# Enable cache
sed -i -e "s/#INVENTREE_CACHE_HOST=inventree-cache/INVENTREE_CACHE_HOST=inventree-cache/g" .env
sed -i -e "s/#INVENTREE_CACHE_PORT=6379/INVENTREE_CACHE_PORT=6379/g" .env
# Use direct directory mapping to avoid mounting issues
sed -i -e "s%- inventree_data:%- $(pwd)/inventree_data:%g" docker-compose.yml
# ... now we can remove the volume declarations from docker-compose.yml
sed -i -e '/^volumes:/,$d' docker-compose.yml

sed -z -i -e 's#profiles:\s*- redis\s*##g' docker-compose.yml # Make redis start always, even without docker-compose --profile redis
# Use standard docker-compose directory naming to facilitate multiple parallel installations
sed -z -i -e 's#container_name:\s*[a-zA-Z0-9_-]*\s*##g' docker-compose.yml # Remove container_name: ... statements
# Create data directory which is bound to the docker volume
mkdir -p inventree_data
# Initialize database
docker-compose up -d inventree-cache inventree-db # database initialization needs cache
docker-compose run inventree-server invoke update

After that, you can check .env for the randomly generated  INVENTREE_ADMIN_PASSWORD and INVENTREE_WEB_PORT.

Now you can enable autostart & start the service using systemd, for more details see our post Create a systemd service for your docker-compose project in 10 seconds:

curl -fsSL https://techoverflow.net/scripts/create-docker-compose-service.sh | sudo bash /dev/stdin

Don’t forget to configure your reverse proxy to point to InvenTree.

Posted by Uli Köhler in Docker, InvenTree

How to fix InvenTree API remote_image: Server responded with invalid status code: 403

Problem:

While trying to create a Company using the InvenTree API you see an error message like

requests.exceptions.HTTPError: {'detail': 'Error occurred during API request', 'url': 'https://inventree.techoverflow.net/api/company/', 'method': 'POST', 'status_code': 400, 'body': '{"remote_image":["Server responded with invalid status code: 403"]}', 'headers': {'AUTHORIZATION': 'Token 340fdf063d5433b83bc37c50a4b52ee2f246021b'}, 'params': {'format': 'json'}, 'data': {'name': 'DigiKey', 'website': 'https://www.digikey.de/', 'remote_image': 'https://www.digikey.de/-/media/Images/Header/logo_dk.png', 'description': 'Test', 'is_manufacturer': False, 'is_supplier': True, 'is_customer': False, 'currency': 'EUR'}}

Solution:

The core issue here is:

{"remote_image":["Server responded with invalid status code: 403"]}

InvenTree tries to download the remote_image URL – but that URL can’t be downloaded and the upstream server (digikey.de in this case) generates a 403 response. This is often the case when an URL can only be downloaded with correct Referer headers or other headers set to a specific value.

In order to fix this issue, change the remote_image URL to an image URL which can be fetched correctly.

Sometimes this issue can also arise if the IP address of the InvenTree server is blocked by the server serving the image. This can easily found out by trying to download the given image URL from the server running inventree using wget [URL].

Posted by Uli Köhler in InvenTree

STM32 HAL equivalent of Arduino millis()

The equivalent of Arduino’s millis() function when using the STM32 HAL is

HAL_GetTick()

The ticks occur once every millisecond, so this will also give you a millisecond timer that will overflow after some time equivalently to how millis() overflows.

Posted by Uli Köhler in Arduino, STM32

How to fix “adb pair” error: unknown host service

Problem:

While trying to adb pair to your device, you see an error message like

$ adb pair 10.9.1.104:34879
Enter pairing code: 232521
error: unknown host service

and the pairing fails

Solution:

You are not running the (correct version?) of the ADB server.

Run

adb kill-server

and then retry your adb pair command.

If that doesn’t work, reboot your device.

Posted by Uli Köhler in Android

How to fix Angular NullInjectorError: No provider for HttpClient!

In case you see the following error for your Angular application in your JS console:

main.ts:6 ERROR NullInjectorError: R3InjectorError(AppModule)[MyService -> HttpClient -> HttpClient -> HttpClient]: 
  NullInjectorError: No provider for HttpClient!
    at NullInjector.get (core.mjs:7599:27)
    at R3Injector.get (core.mjs:8020:33)
    at R3Injector.get (core.mjs:8020:33)
    at R3Injector.get (core.mjs:8020:33)
    at injectInjectorOnly (core.mjs:634:33)
    at Module.ɵɵinject (core.mjs:638:60)
    at Object.MyService_Factory [as factory] (my.service.ts:8:38)
    at R3Injector.hydrate (core.mjs:8121:35)
    at R3Injector.get (core.mjs:8009:33)
    at ChainedInjector.get (core.mjs:12179:36)

you need to add HttpClientModule to your app.module.ts.

First, import HttpClientModule at the top of app.module.ts:

import { HttpClientModule } from '@angular/common/http';

After that, add

HttpClientModule,

to the imports: [...] section of your @NgModule, for example:

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    HttpClientModule,
    BrowserModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

After that, your error should have disappeared

Posted by Uli Köhler in Angular, Typescript

Angular HttpClient JSON service minimal example

Using Angular’sHttpClient  in a service to fetch a JSON is pretty straightforward. In this example, we’ll fetch a JSON array.

Note that it’s recommended to create your own TypeScript interface for improved static typing, but nevertheless starting with any[] is often a good idea since it allows you to get started quickly.

public loadMyJSONArray(): Observable<any[]> {
  return this.http.get<any[]>(`/api/my-api`);
}

Full service example

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class MyService {

  constructor(private http: HttpClient) {
  }

  public loadMyJSONArray(): Observable<any[]> {
    return this.http.get<any[]>(`/api/my-api`);
  }

}

 

 

Posted by Uli Köhler in Angular, Typescript

How to auto-restart bottle server when Python file changes using nodemon

Assuming you have a Python script server.py that you want to auto-reload every time the file changes, use the following script using nodemon:

nodemon -w . --exec python server.py

The -w . tells  nodemon files to watch for changes of all files in the current directory (.)

I generally recommend creating a script start.sh to automatically run this command:

#!/bin/sh
nodemon -w . --exec python server.py

and then make it executable using

chmod a+x start.sh

Now you can run it using

./start.sh

 

Posted by Uli Köhler in Python
This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Cookie settingsACCEPTPrivacy &amp; Cookies Policy