onsdag 9 oktober 2024

Seeed Respeaker HAT support for Raspberry Pi 5

Guide: Seeed ReSpeaker HAT on Raspberry, latest kernel as of October 2024.

Background
I post this because I can't find anyone who have documented a current working solution for RPi 4 or 5 (as of October 2024) for the latest kernel. The installations are known for not working anymore when new kernels and updates are released.

This guide regards 2-mic, 4-mic and 6-mic card configurations.

Seeed, the manufacturer ended their support to these cards for Raspberry years ago but still sell the cards. 

Status 
This project is still experimental. I've done tests by reflashing the OS and installed. Every time I had to reboot and install again for the audio to work. 

For who? 
Potentially for all Raspberry Pi versions with the latest kernel installed and fully updated. 

What does it do?
It expands on HinTak's work with the explicit aim of supporting current shipping Raspbian/Ubuntu kernels without requiring downgrading. 
A expanded installation script has been added which calls the HinTak's installation script as a step in the process.

This script expands on the original installation script. It performs several tasks to ensure proper installation and compatibility with updated kernel versions. Here's a summary of what the script does:

  1. Checks for root privileges and sufficient space on the /boot volume.
  2. Verifies that the system is ARM/Raspberry Pi related by checking for specific directories and commands.
  3. Checks and potentially updates kernel headers to match the running kernel version.
  4. Updates and installs required packages, including kernel headers, gcc, git, and audio-related tools.
  5. Installs a kernel module named "seeed-voicecard" using DKMS (Dynamic Kernel Module Support) to ensure it works across kernel updates.
  6. Copies device tree overlay files (.dtbo) for different microphone configurations to the appropriate directory.
  7. Removes an old ALSA plugin and updates kernel module configurations.
  8. Modifies the boot configuration file to enable I2C and I2S interfaces.
  9. Installs configuration files for the voice card in /etc/voicecard.
  10. Sets up a Git repository in /etc/voicecard to track configuration changes.
  11. Installs a systemd service for the voice card and enables it to start on boot.
  12. Provides instructions for the user to reboot the system to apply all settings.

This script aims to provide a more robust installation process that adapts to potential kernel version mismatches and ensures all necessary components are in place for the voice card to function properly on a Raspberry Pi system.

What works? 
ALSA I/O has been tested and seems to be working. You can test yourself from the menu after installing, as the menu provides a section dedicated to audio and from here you can run suitable commands to show information and execute tests. Note that it is still experimental you're encouraged to do tests manually too by for example looking them up with an AI search engine like Perplexity.

PulseAudio
PulseAudio is not installed by default even though there's still a folder from reaspeaker's original repository. You can install it through the menu system.

PulseAudio is not tested. This is tested on 2-mic voicecard.

Link to fork on Github where you can read more and install it with the help of the guide: https://github.com/Wartem/seeed-voicecard

fredag 27 september 2024

Remote Headless Raspberry Pi OS Lite Setup: No SD Card Reader, No Monitor, No Keyboard

Headless Raspberry Pi OS Flashing Guide

This guide provides a headless method for flashing a Raspberry Pi OS onto a bootable device by running on another another connected device. This approach is especially useful when you don't have direct access to the bootable device from your main computer.

Introduction

The goal of this guide is to help users flash a Raspberry Pi OS to an SSD, SD card, or other bootable device in a headless environment. This method uses another Raspberry Pi to flash the device, configure essential settings, and ensure successful booting from the new OS.

There are certain limitations to consider, such as the inability to boot from a USB when an NVMe SSD is connected. Based on personal experience, you need to flash a microSD card first and boot from it to get the OS onto the SSD.

Additionally, some manual changes may be necessary after flashing to ensure bootability, as the CLI version of the Raspberry Pi Imager doesn’t include customized settings.

For a More Comprehensive Guide

For a more comprehensive guide on headless Raspberry Pi OS flashing, visit

this GitHub repository.

Download and run the script provided, and be sure to read the README for detailed instructions.

Requirements

Before starting, ensure you have the following:

  • A Raspberry Pi that you will use to flash the OS (with SSH access).
  • A bootable device (e.g., NVMe SSD, SD card, or USB drive).
  • Pre-downloaded Raspberry Pi OS image (e.g., raspios_lite_arm64_latest).
  • wget https://downloads.raspberrypi.org/raspios_lite_arm64_latest
  • mv raspios_lite_arm64_latest raspios_lite_arm64_latest.xz
  • Network access to configure Wi-Fi settings on the bootable device.
  • A basic understanding of using the terminal and SSH.

Steps to Flash Raspberry Pi OS

The following YAML-based Ansible playbook automates the process of flashing the Raspberry Pi OS onto a bootable device. The playbook also configures essential settings like SSH, Wi-Fi, and user credentials, ensuring that the newly flashed device is ready to use after boot. Make sure to customize the playbook before running it on your device.

Note: Running this playbook will make changes to your system and the target bootable device. Always double-check your settings and make sure you're targeting the correct device before running the playbook.

Playbook for Flashing OS


---
# Run with sudo ansible-playbook notebook_setup.yml
- name: Set up Raspberry Pi bootable device
  hosts: localhost
  become: yes
  vars:
    bootable_device: /dev/nvme0n1 # Change this to your device path, e.g., /dev/sda, /dev/mmcblk0, etc.
    os_image_path: /home/os_username/raspios_lite_arm64_latest.xz
    wifi_ssid: "WIFI_SSID"
    wifi_password: "WIFI_PASSWORD"
    new_username: "NEW_USERNAME"
    new_password: "NEW_PASSWORD"
    country: "US"
    perform_full_wipe: true
    fixed_wait_time: 60 # Adjust waiting time if needed

  vars_prompt:
    - name: confirmation
      prompt: "Are you sure you want to flash {{ bootable_device }}? All current data on this device will be lost. Type 'yes' to confirm, or 'no' to cancel."
      private: false
      default: ""

  pre_tasks:
    - name: Verify confirmation
      fail:
        msg: "Operation cancelled by the user. Playbook execution aborted."
      when: confirmation != 'yes'

    - name: Check if OS image file exists
      stat:
        path: "{{ os_image_path }}"
      register: os_image_stat

    - name: Fail if OS image doesn't exist
      fail:
        msg: "The specified OS image {{ os_image_path }} does not exist."
      when: not os_image_stat.stat.exists

  tasks:
    - name: Ensure partitions are unmounted
      mount:
        path: "{{ item }}"
        state: unmounted
      loop:
        - /mnt/boot
        - /mnt/rootfs
      ignore_errors: yes

    - name: Clear partition table (optional full wipe)
      command: "dd if=/dev/zero of={{ bootable_device }} bs=512 count=1"
      when: perform_full_wipe | default(false)

    - name: Install required packages
      apt:
        name:
          - rpi-imager
          - parted
        state: present
        update_cache: yes

    - name: Check if bootable device exists
      stat:
        path: "{{ bootable_device }}"
      register: bootable_device_stat

    - name: Fail if bootable device doesn't exist
      fail:
        msg: "The specified bootable device {{ bootable_device }} does not exist."
      when: not bootable_device_stat.stat.exists

    - name: Determine partition suffix
      set_fact:
        partition_suffix: "{{ 'p' if '/dev/mmcblk' in bootable_device or '/dev/loop' in bootable_device or '/dev/nvme' in bootable_device else '' }}"

    - name: Flash new OS to bootable device
      command: >
        rpi-imager --cli {{ os_image_path }} {{ bootable_device }} --enable-writing-system-drives
      register: flash_result
      failed_when: flash_result.rc != 0
      changed_when: flash_result.rc == 0

    - name: Wait for system to settle after flashing
      pause:
        seconds: "{{ fixed_wait_time }}" # Replaces the dynamic partition detection loop

    - name: Inform kernel of partition table changes
      command: "partprobe {{ bootable_device }}"
      ignore_errors: yes

    - name: Ensure partitions are available
      wait_for:
        path: "{{ bootable_device }}{{ partition_suffix }}{{ item }}"
        state: present
        timeout: 30
      loop:
        - "1"
        - "2"
      register: wait_result
      ignore_errors: yes

    - name: Check partition availability results
      fail:
        msg: "Partition {{ bootable_device }}{{ partition_suffix }}{{ item.item }} is not available after 30 seconds."
      when: item.failed
      loop: "{{ wait_result.results }}"

    - name: Debug partition availability
      debug:
        msg: "Partition {{ bootable_device }}{{ partition_suffix }}{{ item.item }} status: {{ item.state }}"
      loop: "{{ wait_result.results }}"

    - name: Fail if partitions are not available
      fail:
        msg: >-
          Partitions are not available after flashing. Please check the device and try again.
      when: wait_result is failed

    - name: Create mount points
      file:
        path: "{{ item }}"
        state: directory
      loop:
        - /mnt/boot
        - /mnt/rootfs

    - name: Mount bootable device partitions
      mount:
        path: "{{ item.path }}"
        src: "{{ item.src }}"
        fstype: "{{ item.fstype }}"
        state: mounted
      loop:
        - path: /mnt/boot
          src: "{{ bootable_device }}{{ partition_suffix }}1"
          fstype: vfat
        - path: /mnt/rootfs
          src: "{{ bootable_device }}{{ partition_suffix }}2"
          fstype: ext4

    # Remaining tasks are unchanged
    - name: Set up Wi-Fi configuration
      copy:
        content: |
          country="{{ country }}"
          ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
          update_config=1

          network={
            ssid="{{ wifi_ssid }}"
            psk="{{ wifi_password }}"
          }
        dest: /mnt/boot/wpa_supplicant.conf

    - name: Enable SSH
      file:
        path: /mnt/boot/ssh
        state: touch

    - name: Ensure SSH password authentication is enabled
      lineinfile:
        path: /mnt/rootfs/etc/ssh/sshd_config
        regexp: "^#?PasswordAuthentication"
        line: PasswordAuthentication yes

    - name: Disable SSH root login
      lineinfile:
        path: /mnt/rootfs/etc/ssh/sshd_config
        regexp: "^#?PermitRootLogin"
        line: PermitRootLogin no

    - name: Check if user exists
      command: "chroot /mnt/rootfs /bin/bash -c 'id -u {{ new_username }}'"
      register: user_exists
      failed_when: false
      changed_when: false

    - name: Remove existing user if present
      command: "chroot /mnt/rootfs /bin/bash -c 'userdel -r {{ new_username }}'"
      when: user_exists.rc == 0

    - name: Create user on the new system
      shell: |
        chroot /mnt/rootfs /bin/bash -c "
        useradd -m -s /bin/bash {{ new_username }} &&
        echo '{{ new_username }}:{{ new_password }}' | chpasswd &&
        usermod -aG sudo {{ new_username }}
        "

    - name: Allow passwordless sudo for the new user
      copy:
        content: "{{ new_username }} ALL=(ALL) NOPASSWD:ALL"
        dest: "/mnt/rootfs/etc/sudoers.d/{{ new_username }}"
        mode: "0440"

    - name: Enable SSH service on the new system
      shell: |
        chroot /mnt/rootfs /bin/bash -c "
        systemctl enable ssh
        "

    - name: Configure network on the new system
      copy:
        content: |
          allow-hotplug eth0
          iface eth0 inet dhcp
          allow-hotplug wlan0
          iface wlan0 inet dhcp
            wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
        dest: /mnt/rootfs/etc/network/interfaces

    - name: Set hostname
      copy:
        content: raspberrypi
        dest: /mnt/rootfs/etc/hostname

    - name: Configure hosts file
      copy:
        content: |
          127.0.0.1 localhost
          127.0.1.1 raspberrypi

          # The following lines are desirable for IPv6 capable hosts
          ::1     localhost ip6-localhost ip6-loopback
          ff02::1 ip6-allnodes
          ff02::2 ip6-allrouters
        dest: /mnt/rootfs/etc/hosts

    - name: Set locale
      copy:
        content: en_US.UTF-8 UTF-8
        dest: /mnt/rootfs/etc/locale.gen

    - name: Generate locale
      command: chroot /mnt/rootfs /bin/bash -c 'locale-gen'

    - name: Set default locale
      copy:
        content: LANG=en_US.UTF-8
        dest: /mnt/rootfs/etc/default/locale

    - name: Set timezone to Eastern Time (US)
      command: >-
        chroot /mnt/rootfs /bin/bash -c 'ln -sf /usr/share/zoneinfo/America/New_York /etc/localtime'

    - name: Unmount partitions
      mount:
        path: "{{ item }}"
        state: unmounted
      loop:
        - /mnt/boot
        - /mnt/rootfs

    - name: Final sync before reboot
      command: sync

    - name: Notify about success
      debug:
        msg: >
          Raspberry Pi bootable device created successfully with OS image {{ os_image_path }} on {{ bootable_device }}.
    - name: Remind user to safely remove device
      debug:
        msg: "Setup complete. You can now safely remove {{ bootable_device }} and boot your Raspberry Pi."


Running the Ansible Playbook

After creating the YAML file with the provided playbook content, follow these steps to run it:

  1. Save the playbook: Save the YAML content to a file named flash_raspberrypi.yml on your Raspberry Pi.
  2. Recommended: Update your RPi in order for installation to succeed:
    sudo apt update && sudo apt upgrade -y
  3. Install Ansible: If you haven't already, install Ansible on your Raspberry Pi:
    sudo apt update
    sudo apt install ansible
  4. Customize the playbook: Before running, make sure to customize the variables in the vars section of the playbook to match your specific requirements:
    • bootable_device: Set this to the correct device path (e.g., /dev/sda, /dev/nvme0n1)
    • os_image_path: Ensure this points to the correct location of your Raspberry Pi OS image
    • wifi_ssid and wifi_password: Update these with your Wi-Fi credentials
    • new_username and new_password: Set these to your desired values for the new user
  5. Run the playbook: Execute the playbook using the following command:
    ansible-playbook flash_raspberrypi.yml
    You may need to run this with sudo if your user doesn't have the necessary permissions:
    sudo ansible-playbook flash_raspberrypi.yml
  6. Monitor the output: Ansible will display the progress of each task in the playbook. Watch for any errors or warnings.
  7. Verify completion: Once the playbook finishes, you should see a success message indicating that the bootable device setup is complete.
  8. Reboot: After the playbook has run successfully, reboot your Raspberry Pi and ensure it boots from the newly flashed device.

Connect to Your New System

When trying to SSH into your new system, you might get an SSH error due to the new OS. To fix this: 1. Remove the old SSH key: ssh-keygen -R your_raspberry_pi_ip_address Replace your_raspberry_pi_ip_address with your Raspberry Pi's actual IP address. 2. You should now be able to SSH into your Raspberry Pi using the username and password you set up earlier. After your first login, it's a good practice to update your system: sudo apt update && sudo apt upgrade -y

Fix the locale


  sudo locale-gen en_US.UTF-8
  # Or for example another country:
  # sudo locale-gen sv_SE.UTF-8
  sudo update-locale LANG=en_US.UTF-8
  
  sudo reboot
  

Conclusion

After following the steps outlined above, your Raspberry Pi OS should be successfully flashed to the bootable device, ready for use. Be sure to configure Wi-Fi settings and enable SSH for easier headless access in the future.

onsdag 5 april 2023

"Deep Blue Swag" by "Eminem" (Chat GPT 4)

Chat GPT is very useful indeed. It can do a lot of things, both useful and funny.

I asked Chat GPT to write a song by "Eminem" about the Blue Whale.

I thought it was quite impressive.

Here goes:


"Deep Blue Swag" by "Eminem" (Chat GPT 4)


[Verse 1]

Yo, I'm cruisin' through the ocean, feelin' that sway,

Ridin' waves like a gangsta, lookin' for a way.

See the biggest homie, got me trippin', no lie,

Blue whale so huge, touchin' the sky.


Floatin' smooth like a lowrider, no tension,

Its size and power, man, it's street comprehension.

A hundred feet long, like a block in the hood,

200 tons of weight, ain't nothin' that could.


[Chorus]

Deep blue swag, you own the sea,

Livin' large, the illest life, you're the MVP.

Rhyme and swim, we're tight, like family,

I'm vibin' with the realest, the blue whale, you and me.


[Verse 2]

Eatin' krill like it's fast food, they ain't playin',

Four tons a day, man, they ain't delayin'.

Biggest homies in the sea, but gentle, no doubt,

Chillin' in the deep blue, ain't no sellout.


Faced tough times, hunted, nearly wiped out,

But they bounced back, yo, they stood their ground.

Now they're risin', a symbol of hope,

Their strength and hustle, man, it's straight-up dope.


[Chorus]

Deep blue swag, you own the sea,

Livin' large, the illest life, you're the MVP.

Rhyme and swim, we're tight, like family,

I'm vibin' with the realest, the blue whale, you and me.


[Bridge]

The ocean's depth like the streets I've known,

These whales and my rhymes, connected, we've grown.

Together we'll rise, conquer this world,

The story of the blue whale, let it be heard.


[Verse 3]

Watchin' 'em swim, I'm feelin' mad respect,

Their power, their story, makes my mind reflect.

Inspired by their vibe, their hustle, their game,

A livin' example, don't give up, no shame.


[Chorus]

Deep blue swag, you own the sea,

Livin' large, the illest life, you're the MVP.

Rhyme and swim, we're tight, like family,

I'm vibin' with the realest, the blue whale, you and me.


[Outro]

So here's to the blue whale, the boss of the deep,

Their story's a treasure, one we gotta keep.

I'll carry their vibe in the rhymes I spit,

A shout-out to the blue whale, the ocean's legit. 

onsdag 28 december 2022

Multiplication of sequences like lists, tuples and str | Python

The multiplication operator on a ordered collection (sequence), like strings, lists, tuples, byte sequences or byte arrays means that the items/objects inside are repeated. It does not mean creation of copies (shallow or deep ones) of the items/objects.

This can be tested with the help of the id() function which returns the memory address of an object. Two objects with overlapping lifetime can never have the same id.

An exception to this is small intergers and other immutables, which Python keeps a small cache of hence giving them the same ID.


Example 1:

```

a = [1]

b = [1]

print(id(a), id(b))

```

Output: 2 different IDs because no cashing. 


Example 1.1:

```

a = 1

b = 1

print(id(a), id(b))

```

Output: Same ID for a and b, because of cashing.


Example 1.2 Same ID because of repeat:

```

a = [1]

# Multiply by 2 to get [1, 1]

a = a * 2 

for num in a:

    print(id(a))

```

Output: (Same ID x 2):

140719374866624

140719374866624

onsdag 9 november 2022

[Python] len() and time complexity

Some say "if you care about speed you shouldn't use Python". Others say "since Python isn't fast, you have to optimize it to make it viable".


The behavior of len() depends on the class definition.

Calling len() is always taking constant time for iterable data structures(string, list, tuple, etc.). That's because they have __len__() defined that way. They use a counter which is altered when the iterable in question is altered. len() is therefore just returning a counter in this scenario.

Without defining __len__() you can't use len() on it. Since it's up to the creator of the class to define what this dunder method should do and return, you can have any time complexion. You could for example traverse something every time the method is called. 

måndag 7 november 2022

Pygame for Python 3.11

As of writing this, Python 3.11 is a new release and in order to use it with Pygame you have to install Pygame in slightly different way:

pip install pygame --pre

fredag 4 november 2022

How to learn & three great Python books

 I recently found an interesting article about learning:

https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5780548

From this paper:

Six strategies for effective learning:

1. Spaced practice - Instead of studying it all within a short time.

2. Interleaving - Switching between topics while studying.

3. Retrieval practice - Bringing learned information to mind from long-term memory.

4. Elaboration - Asking and explaining why and how things work.

5. Concrete examples - illustrating abstract concepts with specific examples.

6. Dual coding - Combining words with visuals.


For me, learning by doing works great. It makes it more fun and engaging.

For this I recommend the Python Cookbook: Recipes for Mastering Python 3 by Brian K. Jones and David M. Beazley.


Other great intermediate level books are Fluent Python and Effective Python: 90 Specific Ways to Write Better Python by Brett Slatkin.






måndag 17 oktober 2022

How to convert a .py file into .exe

1. Run pip install pyinstaller globally and also in your venv.

2. While using venv, type pyinstaller --onefile -w 'filename.py' in your terminal while being in the same directory as your filename.py.

3. Your .exe etc are now created!


The above worked for me. I read long guides on how to do this but this seems to be enough.

torsdag 22 september 2022

Python | Getting steering wheel to work in Arcade

Steering wheels in pygame and arcade go under the name "joysticks". I was able to get my Logitech G27 Racing Wheel to work with pygame but not with Arcade. I made a project using Arcade and used the joystick handling from pygame. 


General idea of the code, important parts:

joysticks = None

def init():
    pygame.init()
    pygame.joystick.init()

    global joysticks
    joysticks = [pygame.joystick.Joystick(x) for x in range(pygame.joystick.get_count())]


class MyGame(arcade.Window):

    def __init__(self):
    ...

    '''
    Use Logitech G27 Racing Wheel if found, else the first connected, if
    any.
    '''

    self.joy_in_use = None

    if joysticks:  
            for joystick in joysticks:  
                if "Logitech G27 Racing Wheel USB" in joystick.get_name():
                    self.joy_in_use = joystick  
               
            if not self.joy_in_use:
                self.joy_in_use = joysticks[0]
       
        if self.joy_in_use:
            self.joy_in_use.init()


    def arcade_joystick_events(self):
        for event in pygame.event.get(): # User did something.
               
            if event.type == JOYBUTTONDOWN:
                input = event.button
                if (input == 0 or input == 6):
                    # code for player jump
           
            if event.type == pygame.JOYAXISMOTION:
                if joysticks:
                    if (axis_val := self.joy_in_use.get_axis(0)) > 0.2:
                        # code for player moving right
                        # axis_val can be used to affect moving speed.
                        
                    elif axis_val < - 0.2:
                        # code for player moving left
                    else:
                        # code for player standing still
       
        pygame.time.wait(10)


    def on_update(self, delta_time):
        self.arcade_joystick_events()
        ...

torsdag 18 augusti 2022

Python | Avoid IndexError by slicing

"IndexError: list index out of range" can for example be avoided by doing [0:1] instead of [0] 

Let's say you're handling iterators that sometimes are empty and do something like "...if list_ else []". 

You're doing this to avoid trying to access an index that doesn't exist when the list is empty, like this: [][0].


If you don't need the item directly and it's ok to return an iterable, an easier or at least shorter way of returning empty iterable can be to return a sliced version of it. 

[][:1] returns an empty list []

["Test"][:1] returns ["Test"]


Other working examples:[][0:0], [][1:0], [][0:2] [][100:25], [][25:100]

These all return [].




torsdag 4 augusti 2022

Decorators in Python

Decorator is a good name to describe what it does although the concept is a bit confusing at first. 

When a decorator has been made, it can potentially be used on any function you like. For example, you can extend/enhance your function to make it calculate how long it takes to run it.
All you have to do is adding "@your_decorator_name" just above the function you like to extend.
This means that additional code will be run, using your function in whatever way the decorator is defined to run it. And it uses the same arguments.

Example:


import functools

def your_decorator(base_func):
    @functools.wraps(base_func)
    # extended_func will be called instead of base func
    # since it's returned and replaces base func
    def extended_func(input_from_base_func):
        print("Printed inside the extended func")
        # This will call will_be_extended
        base_func(input_from_base_func)
        return "Returned when will_be_extended is called"
    return extended_func

@your_decorator
def will_be_extended(used_by_extension):
    # Do something
    print("Printed from base function")
    # This prints: "Sent to base func from decorator"
    print(used_by_extension)
   
# will_be_extended is called but the extended_func is returned
# automatically instead, including the text argument.
from_extended_func = will_be_extended("Sent to base func from decorator")

# This will print the returning value of extended_func,
# "Returned when will_be_extended is called"
print(from_extended_func)

 

måndag 1 augusti 2022

Python - Journey from intermediate to expert Pythonista

 As with any programming language you learn, you need a roadmap. It might not be obvious how to personalize such a path.

Things to take into consideration is for example prior knowledge and which format that works best (video or text).

The basics is similar between many languages. Most courses starts from the very basics and it can sometimes be hard to skip content in a course or tutorial.

Today there's a ton of readily available both free and paid learning resources online. There's an argument to be made that for motivated, self-learning is enough. Many employers care more about what skills and knowledge you actually have and less about which schools you've graduated from.

I started learning Python a few months back and I would like to share my views on the learning experience so far.


My general opinions regarding learning programming:

Videos

Great for explaining difficult topics.

Articles

Great for a more in-depth understanding about specific subjects.

Books: 

Great as guided tours on laid out paths. Make sure it's the right path before investing your time in a book that might not be for you. 

Podcasts:

Great when doing something else.

Apps (Android/iOS):

Great for studying when you're not at home.

Projects:

Creating projects is incredibly important. This is the way to actually learn what you've consumed and read. Doing projects is what makes you actually learn and memories.

Problem solving exercises like leetcode, codewars or checkio:

Solving engaging challenges and fun tasks is nice a complement to your studies. It also gives a confidence boost and lets you know your skill level.  You can share your code and interact with others. 

Other thoughts:

If possible, finding a mentor gives motivation a boost. Same goes with forums, slack and discord. 

Knowing how to do research and use search engines efficiently is crucial. More or less all questions you might have is already answered.

As with everything, a clear goal with what you're doing and why you learn is essential for motivation. Maybe becoming a data analyst even data scientist? 


Finding a path

As someone with a bachelor in computer science and experience with languages like Java and C++, doing basic Python tutorials wasn't ideal. I have tried many services online. Here are my recommendations:

CS50's Introduction to Programming with Python[Video format] (Free) (harvard.edu/course/cs50s-introduction-programming-python)

I took this course after watching this review by "Python Programmer": 


CS50 will be challenging for someone new to programming. With prior knowledge you can increase the playback speed.

Real Python [Basic to Master] (Paid and Free) realpython.com

I just bought a subscription. This site seems to have it all. Articles, videos, learning paths, community, slack, updates, quizzes, Q&A with experts, books (cost extra) and more.

Example of article: realpython.com/python-time-module


App [Basic to Intermediate] (15 days Free) "Sololearn" (Android/iOS) 

It is a little hard to type on the phone but the material was incredible and was made into a fun game-like experience with leaderboards and certificates.

Try to complete these courses: Python Data Structures, Intermediate Python and Python for Data Science. The SQL course is also really good. 


Books

So far my favorite books are (intermediate level) "Fluent Python" and "Python tricks the book".


Podcast

There are many but one that I've listen to a lot is realpython.com/podcasts/rpp/


Some Youtube recommendations

Corey Schafer youtube.com/c/Coreyms

Fireship youtube.com/c/Fireship

Python Engineer youtube.com/c/PythonEngineer/

Coding Tech youtube.com/c/CodingTech

Python Programmer youtube.com/c/FlickThrough

freeCodeCamp.org youtube.com/c/Freecodecamp 

fredag 22 juli 2022

From a list, count how many numbers are within the standard deviation from the mean. (Python)

heights = [180, 172, 178, 185, 190, 195, 192, 200, 210, 190]

height_sum = sum(heights)
mean = height_sum / len(heights)

# **2 makes negatives (heights less than the mean) become positive.
diff_list = [(x - mean) ** 2 for x in heights]
variance = sum(diff_list) / len(heights)

# We get the square root in order to get back the original units.
standard_deviation = variance **0.5

# Now we just need to find out how many heights are within the
# standard deviation from the mean.
count = 0
upper_lim = mean + standard_deviation
lower_lim = mean - standard_deviation

# Count how many heights are within the
# standard deviation from the mean.
for height in heights:
    if lower_lim < height < upper_lim:
        count += 1
       
print(count)






''' Or as a class, initiated with the heights list: '''

class Std():
   
    def __init__(self, data: list):
        self.data = data
        self.mean = self._get_mean()
        self.var = self._get_variance()
        self.std = self._get_std_from_var()
       
        self.upper_lim = self.mean + self.std
        self.lower_lim = self.mean - self.std
   
        self.in_range = lambda x: self.lower_lim < x < self.upper_lim
        self.count_within = len(list(filter(self.in_range, self.data)))

    def _get_mean(self):
        return sum(self.data) / len(self.data)

    def _get_variance(self):
        diff_list = [(x - self.mean) ** 2 for x in self.data]
        return sum(diff_list) / len(diff_list)

    def _get_std_from_var(self):
        return self.var **0.5

std = Std(heights)
print(std.count_within)

måndag 11 juli 2022

OpenArtViewer

I've made an art viewer program in Unity. I'm now working on database update automatization in Python. That is because the files with art information provided are updated frequently.

OpenArtViewer uses the National Gallery of Art Open Data API and data from Metropolitan Museum of Art. The included SQLite database has stored info about the artworks in public domain (100K+) including image links to the artworks on the NGA and MM servers. 

From the GUI you can search type of art and by title, year, artist. To the left there's a scroll bar with the results as thumbnails. These can be clicked to get more information and a higher resolution version of the artwork in the big panel to the right. A high resolution image can then be downloaded. 

Download this program (Windows) https://wartem.se/files/OpenArtViewer.zip




National Gallery of Art

Metropolitan Museum of Art

lördag 9 juli 2022

Flask - Absolute basic client input and render

The following example shows how you can handle input with Flask and update pages, in this case with the user input sent back to the client. 

I'm making this as short as possible. 

Requirements: pip install Flask 

Implementation: 
1. In your project root dir create: A file named app.py 
2. In your project root dir create: A folder named "templates" and inside a file named "index.html" 


index.html 
<html lang="en"> 
  <head> 
  <title>Flask Basic Example</title>
  </head>  
  <body>    
  <h1>Flask Post: {{ text_to_show }}</h1>    
  <form action="/" method="POST">      
  <input type="text" name="content" />     
  <input type="submit" />    
  </form>  
  </body></html>

app.py
from flask import Flask, render_template, request
app = Flask(__name__)

@app.route("/", methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        user_input = request.form['content']
        return render_template("index.html", text_to_show=user_input)
    return render_template("index.html")

if __name__ == "__main__":
    app.run(debug=True, port=5000)

How it's used: Run app.py and go to http://127.0.0.1:5000 in your browser. Type some input, press send and the server will update the HTML with your input in a HTML paragraph. The Internet is full of extensive guides and tutorials. Sometimes you just want a quick example.

Python Virtual Environment Setup

Working in a virtual environment is easy to setup and makes life easier thanks to the project isolation and dependency management.
Run once:
py -m pip install --user virtualenv
Run in project folder:
py -m venv env
Run in project folder:
.\env\Scripts\activate
Done! Happy coding.

torsdag 25 november 2021

Smooth Zooming with Cinemachine (Unity)

Zooming a Cinemachine Virtual Camera with 3rd Person Follow by changing the camera distance works but there are no transitions between the steps.

There are other ways to zoom that look better.

One way to get it smoother I found out  is to add a extension to your virtual camera called Follow Zoom. It's a script and comes with values you can change in the inspector. The Follow Zoom extension can be added at the bottom of your virtual camera, in the inspector.

You can use the input you use for zoom to alter these values in a script of your own. You use a reference to your virtual camera and do .GetComponent<CinemachineFollowZoom>() to get the Follow Zoom script.

Once you have the Follow Zoom extension you can change the values of these variables to get a desired transition in your zooming:

_cinemachineFollowZoom.m_MinFOV += zoomInput;

_cinemachineFollowZoom.m_MaxFOV += zoomInput;

_cinemachineFollowZoom.m_Width += zoomInput;


ZoomInput above is calculated from the Input Action Asset made in "new" Input System. It's a Vector2, triggered by scrolling and pressing the gamecontroller defined in a created input asset, attached to a Player Input component, added to the player GameObject. Google Unity Input System to learn how to use it. It's not as complicated as it might look.


You will need checks to make sure you stay within the limits set in the inspector.

These conditions aren't allowed:

if (zoomInput > 0 && cineMinFoV >= cineMaxFoV)

if(zoomInput < 0 && cineMinFoV < 1)


I found a value of about 1-4 is pretty good to set "Damping" to in the inspector and Min FOV to 2. Damping needs to be above 0.

måndag 15 november 2021

Unity - How to get access and change the camera distance while using Cinemachine "3rd person follow" (C#)

Cinemachine https://unity.com/unity/features/editor/art-and-design/cinemachine

saves time by letting you create difference kinds of cameras.


Let's say you want to use the input system and scroll the mouse to change the camera distance.

I couldn't find a clear solution online so I thought I share:


First make sure you have:

A player GameObject.

A GameObject with a CinemachineVirtualCamera, set to: 

* follow the player.

* its "Body" set to "3rd Person Follow".


Note: You have other body properties as well to chose from:

3rd Person follow

Framing Transposer

Hard Lock to Target

Orbital Transposer

Tracked Dolly

Transposer


We are focusing on the "3rd Person Follow" now.


In the script that you add as component to the player GameObject:


Declaration: 

private GameObject _playerFollowCamera;

public CinemachineVirtualCamera _cinemachineVirtualCamera;


In private void Start() :

_playerFollowCamera = GameObject.Find("PlayerFollowCamera");

_cinemachineVirtualCamera = _playerFollowCamera.GetComponent<CinemachineVirtualCamera>();


          In your method, called when scrolling for example: 

CinemachineComponentBase cinemachineComponentBase = _cinemachineVirtualCamera.GetCinemachineComponent(CinemachineCore.Stage.Body);


if (cinemachineComponentBase is Cinemachine3rdPersonFollow)

{

(cinemachineComponentBase as Cinemachine3rdPersonFollow).CameraDistance = yourCameraDistanceValue;

}


Or as one line:

(GameObject.Find("PlayerFollowCamera").GetComponent<CinemachineVirtualCamera>().GetCinemachineComponent(CinemachineCore.Stage.Body) 

as Cinemachine3rdPersonFollow).CameraDistance = yourCameraDistanceValue;


"Cinemachine3rdPersonFollow" above is changed depending on which body property you are using.


måndag 4 januari 2016

GTA V: Change vehicle sounds on any vehicle to any sound

It's really easy to change the sounds on any vehicle to any other vehicle sound in GTA V.

Before we start, make sure to use OpenIV with the mods folder for safety: http://openiv.com/?p=1132

This is how you do it:
1. Go to update\update.rpf\common\data\levels\gta5 with OpenIV
2. Right click on vehicles.meta and press "Save content/ Export".
3. Open the file that you exported with notepad.
4. Find the vehicle model you want to change (CTRL+F and then search for modelName). If you are unsure which model it is you can use Native Trainer to spawn them and find out.
5. Look for "<audioNameHash />" under your model name. Replace it with "<audioNameHash>PutYourModelNameHere</audioNameHash>" and instead of "PutYourModelNameHere" you write the name of the model you want to use the sound from. Here are all model names: http://pastebin.com/i4AX8kBY (taken from Alexander Blades Native Trainer source).
6. Save the file, go back to OpenIV and replace the vehicles.meta with your version of it.
7. Start GTA V try it out :)

Tip: A sound that I like a lot is the engine sound of Stirling GT with model name FELTZER3. That would be <audioNameHash>FELTZER3</audioNameHash>

Play GTA V without violence

Family Friendly Free Roaming has been out for a while now. FFFR is a modification for Grand Theft Auto 5 that disables violence in the game. The latest versions has made it even less violent. This means that even children can play it.

Except making the game less violent, the purpose of the mod is to make it more fun exploring the amazing world without interrupting elements.

There are also features like entering vehicles as passenger and busing pedestrians around.

You can read more and get the latest version of FFFR here: https://www.gta5-mods.com/scripts/family-friendly-free-roaming