quasar devSever with own certificate

i just tried to get a localhost certificate to work on my setup-
and after some try and error i got this to work:

using minica to generate a cert:
create folder .certs in your project root

mkdir .certs

navigating inside

yourProject $ cd .certs
yourProject/.certs $ 

create a cert:

yourProject/.certs $  minica -domain localhost

import the root ca minica.pem in firefox or your default browsers..

then add in your quasar.conf

// ....
const fs = require("fs");
// ....
module.exports = configure(function (/* ctx */) {
    return {
// ....
        // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
        devServer: {
            open: false, // opens browser window automatically
            // https: true, // for automagically self-signed cert.
            https: {
                key:  path.join(__dirname, ".certs/localhost/key.pem"),
                cert: path.join(__dirname, ".certs/localhost/cert.pem"),
            },
        },
// ....
});

and that indeed works 🙂

sadly if you are search the web you first will land at
https://quasar.dev/quasar-cli-webpack/quasar-config-file#devserver
(scroll down a bit)
as there is an example of the webpack config.
and in the vite doc there is currently none for this setup..

rename.py

some days ago my brother asked me if i can help him batch rename about 200 pdf files..
the needed name for the file was the heading found on the first page…

so i did a quick and hacky script and experiment with PdfReader library –
it worked really nicely and was only about 2h in learning and getting it to work as it should 😉

#!/usr/bin/env python3

import os
import re

from pathlib import Path

from pypdf import PdfReader

from operator import itemgetter, attrgetter

print(42 * "*")
print("running script rename.py")
print(42 * "*")
print()



# extract ids and title
regex_title = re.compile(
    r"Some Fixed Pre Text \(LM\),\s*(?P<id1>\d+-\d+)\s*(?P<title>.*?)\s*\((?P<id2>\s*G(\d+\s*)+)\)",
    re.IGNORECASE,
)


def parse_file(filename):
    print(42 * "-")
    print(f"reading file '{filename}'")
    reader = PdfReader(filename)
    page = reader.pages[0]

    # print(42*'-')
    # print(f"extracting text from page 0:")
    # print(page.extract_text(extraction_mode="layout"))
    # print(42*'-')

    parts = []

    def visitor_body(text, cm, tm, font_dict, font_size):
        # get top part
        y = cm[5]
        if 600 < y < 1020:
            parts.append(text)

    page.extract_text(visitor_text=visitor_body)
    text_body = "".join(parts)
    # text_body = text_body.replace('\n', ' ').replace('\r', ''
    text_body = " ".join(text_body.splitlines())
    # print(f"extracting text from page 0 with filter:")
    # print(42*'-')
    # print(text_body)
    # print(42*'-')
    # now we have on continus line.
    # let us get all the parts we need with some regex magic:
    regex_result = regex_title.search(text_body)
    # print(42 * "-")
    if regex_result:
        # print(regex_result.groupdict())
        result = regex_result.groupdict()
        result["title"] = result["title"].replace('/', '-')
        result["id2"] = result["id2"].replace(' ', '')
    else:
        result = {"text_body":text_body}
        
    # print(42 * "-")
    return result


def get_filelist():
    p = Path('.')
    filelist = list(p.glob('**/*.pdf'))
    return filelist

def main():
    files = get_filelist()

    results = []

    for file in files:
        result_dict = parse_file(file)
        result_dict["filename"] = file
        # result_dict["birth"] = file.stat().st_birthtime_ns
        result_dict["birth"] = file.stat().st_mtime
        results.append(result_dict)

    # we now have a list of dicts for each file with the extracted title and ids
    # we first sort it.
    # results.sort(key=itemgetter('id1', 'title', 'id2', 'birth'))
    results.sort(key=itemgetter('id1', 'title', 'id2'))

    for result in results:
        # print(result)
        title = result["title"]
        id1 = result["id1"]
        id2 = result["id2"]
        birth = result["birth"]
        filename_old = result["filename"].resolve()
        # create new base filename_new
        stem_new = f"{id1} - {title} - {id2}"
        # print(f"stem_new: '{stem_new}'")
        # modifie the stem part
        filename_new = filename_old.with_stem(stem_new)
        print(filename_new)
        # extend if this one already exists...
        while filename_new.exists():
            filename_new = filename_new.with_stem(filename_new.stem + " - 1")
        filename_old.rename(filename_new)

main()

maybe its of help for others…

maybe just as personal memo.

logging in docker + php

just a quick personal reminder:

hot to log php things inside of docker…

error_log("My Error Message... '" . $somevariable . "'", 0);

Pizza V2

neues Rezept 🙂


2 Bleche, 500g Mehle

– Buchweizenmehl 180g
– Reisvolkornmehl 180g
– Maisstärke 100g
– Braunhirsemehl 40g
– Wasser 360ml
– 1Tüte Weinstein-Backpulver
– eine Gute Priese gemahlene Flohsamenschalen (Pulver)

Umluft 190°C
auf unterster Schiene 15-20min

funny plastic material…

did you know there is a plasict material that melts at about 60°C ?
the official name / main ingredient is
Polycaprolacton (PCL) (wikipedia: de en)
product names i found:

the challeng is to get a good water bath in the right temperature.
so for this i reused my HotPlate SMD soldering hardware.
created a profile that just heats to 60°C and waits…

i will add some pictures and experiment results here in some days 😉



Arduino Light-Barrier with TSOP4438

you just want to build a Light-Barrier (DE: Lichtschranke) ?

for example to measure speed? Or Count goals on a Table-Top-Football?

then you are at the right place.

We use a TSOP4438 as Receiver and some 5mm IR-LED as Sender.

the code

IR_TSOP4438_light_barrier_withDebounce/ir_light_barrier.ino

// based on
// Example of modulating a 38 KHz carrier frequency at 500 Hz with a variable duty cycle
// Author: Nick Gammon
// Date: 24 September 2012
// https://forum.arduino.cc/t/how-to-create-a-38-khz-pulse-with-arduino-using-timer-or-pwm/100217/44

// tweaked for TSOP4438
// https://www.vishay.com/docs/82459/tsop48.pdf
// find max burst length and min gap times:
//
// Minimum burst length 10 cycles/burst
// After each burst of length 10 to 40 cycles
//  a minimum gap time is required of ≥ 10 cycles
// Maximum number of continuous short bursts/second: 1500
//
// translates to:
// 38kHz = 26,3 us / Pulse → *10= 263us
// max burst length: <= 1052us  (26,3*40)
// min gap   length: >=  263us  (26,3*10)
// total cycle time of (1052+263=) 1315us 
// that violates the max bursts/second (666us/burst)
//
// on option is to optimize for the most bursts/second:
// so we use the min gap time as given and use the rest of the available time.
// 666us - 263us = 403 us burst length
// hopefully this way the AGC does not filter our stream...
// we now have to fit this to the best available prescaler / counter values:
// 672us fits good (this way we have less than the 1500 burst)
// this translates to 42 counts a' (0,0625us*256=) 16us
// so we use a gap length of 16us*17 = 272us 
// this gives us a burst length of 16us*(42-17)= 400us
//
// second option is to optimize for the longest burst length and have less bursts/second.
// here we will use a in-between leaning towards longer bursts:
// 0,0625us*256=16us
// 16us*50counts =  800us = 0,80ms = 1250,00Hz = 1250bursts/s
// 16us*80counts = 1280us = 1,28ms =  781,25Hz = 781,25bursts/s
// gap   length: 16us*17      =  272us 
// burst length: 16us*(80-17) = 1008us

// http://www.gammon.com.au/forum/?id=11504
// Timer 1
//   OC1A: D9
//   OC1B: D10
const byte LED = 9;

// Timer 2 (8bit)
//   OC2A: D11
//   OC2B: D3


// Clock frequency divided by 38 kHz frequency desired
const long timer1_OCR1A_Setting = F_CPU / 38000L;
// (16000000 / 38000) = 421,05
// this only works on Timer 1 - as it is a 16bit timer.

// ------------------------------------------
// in-between -  leaning for longer bursts
// target counts:
// CPU          16MHz (0,0625us)
// prescaler    256
// target       781kHz (1280us = 1,28ms)
const long timer2_top = (F_CPU / 256L) / 781L;
// (16000000 / 256) / 781 = 80
// calculate on / off ratio (toggle point)
const long timer2_compare = timer2_top * 1008L / 1280L;
// 80 * 1008 / 1280   =  80 - 17  =  63



volatile bool sender_active = false;

ISR (TIMER2_COMPA_vect) {
    // used to combine the two timers...
    if (sender_active == false) {
        // enable timer1 output
        TCCR1A |= bit(COM1A0) ;  // Toggle OC1A on Compare Match
        // digitalWrite (LED_BUILTIN, HIGH);
        sender_active = true;
    } else {
        sender_active = false;
        // disable timer1 output
        TCCR1A &= ~bit(COM1A0) ;  // DO NOT Toggle OC1A on Compare Match
        digitalWrite (LED, LOW);  // ensure off
        // digitalWrite (LED_BUILTIN, LOW);
    }
}

void lightBarrierSender_setup() {
    pinMode(LED, OUTPUT);
    digitalWrite(LED, LOW);
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, LOW);

    // set up Timer 1 - gives us 38.095 KHz
    TCCR1A = bit (COM1A0); // toggle OC1A on compare
    TCCR1B = _BV(WGM12) | _BV (CS10);   // CTC to OCR1A, No prescaler
    OCR1A =  (16000000L / 38000L / 2) - 1;  // zero relative

    // setup Timer 2
    TCCR2A = 0;
    TCCR2B = 0;
    // toggle OC2A on compare
    // TCCR2A |= bit(COM2A0); 
    // fast pwm to OCR2A
    TCCR2A |= bit(WGM21) | bit(WGM20);
    TCCR2B |= bit(WGM22); 
    // prescaler 1024
    // TCCR2B |= bit(CS22) | bit(CS21) | bit(CS20);
    // prescaler 265
    TCCR2B |= bit(CS22) | bit(CS21);
    // top
    OCR2A = timer2_top - 1;  // zero relative
    // switch point
    OCR2B = timer2_compare - 1;  // zero relative
    // enable interrupts
    TIMSK2 = bit(OCIE2A);
    // TIMSK2 = bit(OCIE2B) | bit(OCIE2A);
}

/IR_TSOP4438_light_barrier_withDebounce/IR_TSOP4438_light_barrier_withDebounce.ino

// simple light barrier test


unsigned long debounceDuration = 10;

const byte beam1Pin = 2;
bool beam1State = LOW;
bool beam1StateLast = LOW;
unsigned long beam1Timestamp = 0;

const byte beam2Pin = 3;
bool beam2State = LOW;
bool beam2StateLast = LOW;
unsigned long beam2Timestamp = 0;



void setup(){ 
    Serial.begin(115200);
    Serial.println("IR_TSOP4438_light_barrier");
    Serial.println("setup...");
    
    pinMode(beam1Pin, INPUT);
    pinMode(beam2Pin, INPUT);

    lightBarrierSender_setup();
    
    Serial.println("running.");
}

void loop() {
    beam1_check();
    beam2_check();
}


void beam1_check() {
    bool beam1Current = digitalRead(beam1Pin);

    if(beam1Current != beam1StateLast) {
        beam1Timestamp = millis();
    }

    if ((millis() - beam1Timestamp) > debounceDuration) {
        // egal welcher wert - dieser ist länger als debounceDuration da!

        // check for state change
        if(beam1Current != beam1State) {
            beam1State = beam1Current;
            if(beam1State) {
                Serial.println("beam1 brock...");
            }
        }
    }

    beam1StateLast = beam1Current;
}

void beam2_check() {
    bool beam2Current = digitalRead(beam2Pin);

    if(beam2Current != beam2StateLast) {
        beam2Timestamp = millis();
    }

    if ((millis() - beam2Timestamp) > debounceDuration) {
        // egal welcher wert - dieser ist länger als debounceDuration da!

        // check for state change
        if(beam2Current != beam2State) {
            beam2State = beam2Current;
            if(beam2State) {
                Serial.println("beam2 brock...");
            }
        }
    }

    beam2StateLast = beam2Current;
}

or just download this arduino sketchbook:

deep dive into the details

To get the TSOP4438 to work we need to send a 38kHz Modulated Signal from the LED. the catch: the receiver is designed to ignore CONTINUOUS signals – that is the way to also reject all the Disturbance from surrounding things like Fluorescent Lamps or other things…

therefore we need to modulate again the 38kHz signal:
so that there are times the signal is send and breaks where the beam is off. The timing requirements for this Pattern are described in the Datasheet :

Minimum burst length 10 cycles/burst

After each burst of length 10 to 40 cycles
a minimum gap time is required of ≥ 10 cycles

For bursts greater than  40 cycles
a minimum gap time in the data stream is needed of  > 10 x burst length

Maximum number of continuous short bursts/second 1500

To Be continued…

Research:

  • https://www.vishay.com/docs/82459/tsop48.pdf#page=6&zoom=250,-155,381
  • https://forum.arduino.cc/t/how-to-create-a-38-khz-pulse-with-arduino-using-timer-or-pwm/100217/12
  • https://forum.arduino.cc/t/how-to-create-a-38-khz-pulse-with-arduino-using-timer-or-pwm/100217/64
  • http://www.gammon.com.au/forum/?id=11504&reply=6#reply6
  • http://www.gammon.com.au/forum/bbshowpost.php?id=11504&page=2
  • http://www.gammon.com.au/images/Arduino/Timer_2.png
  • http://www.gammon.com.au/images/Arduino/Timer_1.png
  • https://arduino.stackexchange.com/questions/31187/irremote-send-and-receive-same-arduino
  • http://www.righto.com/2010/03/detecting-ir-beam-break-with-arduino-ir.html?showComment=1447463463512#c570784324410264988


Vegane Elisenlebkuchen / Nusstaler

Diesmal war die Challenge *Basische* Leckereien für mich zu Zaubern.

es geht in Richtung Elisenlebkuchen – ich würde es eher als Nusstaler benennen.

Lebkuchen / Nusstaler Zubereitung

Zutaten

die meisten Zutaten habe ich von Rapunzel verwendet.

Zutaten

  • 50g Datteln
  • 90g Rosinen
  • 120g Haselnüsse
  • 140g Mandeln
  • 60g Cachewkerne
  • ca 7g Lebkuchengewürz
  • ca 4g Ceylon Zimt
  • ein bisschen geriebene und getrocknete Orangenschale
  • eine Halbe Zitrone (inkl. Schale!!)
  • einen kleinen Schluck Wasser

Zubereitumg

  • Datteln kleiner schneiden
  • Dateln & Rosinen mit sehr heisem Wasser einweichen
  • Nüsse alle zussammen mischen
  • hälfte davon in kleinen portionen mit einem Hexler sehr fein mahlen
  • zweite hälfte gröber hexeln
  • zur Seite stellen
  • Datteln & Rosinen Gewürze & die (zerteilte) Zitrone mit einem kleinen Teil des Einweich-Wassers sehr fein Hexeln
  • dies gibt eine feine creme
  • diese creme mit der Nussmischung verrühren / kneten
  • wenn gleichmäßig verknettet für mehrere Stunden Kühl-Stellen
  • Ofen Vorheizen: Umluft 140°C
  • dann den Teig in kleine Haufen aufteilen
  • diese dann zwischen den Handflächen rollen – dadurch entstehen Kugeln die eine glatte Oberfläche haben
  • jeweils die Kugel mit dem Handballen flachdrücken (ca 1cm Höhe)
  • dann ab in den Backofen (140°C Umluft) – ca 15-20min – leichte bräune = fertig 🙂
  • kurz auskühlen lassen

Frisch schmecken sie mir am besten 🙂
ich finde sie sehr gelungen – das nächste mal etwas mehr zitrone und mehr Zimt –
das macht es Frischer und Würziger.. (das Lebkuchengewürz hatte eine starke Nelke-Note..)

Das Konzept des Zweiteiligen Hexelns wie ich es mir beim letzten mal ausgemahlt habe ist aufgegangen.

Gerne wieder 🙂

actual soldering :-)

this morning i did a last test-run with the tweaked Felder ISO-Cream profile:

yeah… at the top i thought it is in the cooling step already and opened the window – with ~3°C cold air from outside it dropped fast..
then i found it is in the middle of the reflow – sorry… and closed the window again – until it really switched to cooling..

the old left-over pcb i use for these is done now.. i comes from my LEDBoard_4x4_16bit project – and if i remember correctly i backed it with the assembled board in the oven multiple times back then..
now grilled it again ~4-7 times. it smells very bad – is super dark discolored.. i think that is ok with about ~12 times solder cycles..

and then started to assemble a simple board to really test the profile 🙂

placed

then reflowed:

i added a paper-lid to have stable air inside..

reflow was successful 🙂
my profile is just a little bit to long for my right angle touch switches:

they melted away 🙁 – lesson learned – have a look in the datasheet and you know that they are very heat sensitive!

in general i have the feeling that my heating elements get a little bit to hot – the pcb also slightly discolored at on place…
so will keep an eye on this and improve it..

Open Points

  • add housing
    • i would like to have class at the top for a good view what is happening inside..
  • metal frame for heating-elements
  • quite 5V fan with PWM control for cooling
  • add second temperature sensor
  • spring thing to hold board down
  • way to fix sensor position on board
  • more heating elements for bigger working area
    • switchable configuration for long or more square pcbs?!
  • bigger / second power supply ?! (~750W)