diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..83486a1 --- /dev/null +++ b/404.html @@ -0,0 +1 @@ +404
404
Not Found
« back to home »
\ No newline at end of file diff --git a/blog/802-15-4-wireless/index.html b/blog/802-15-4-wireless/index.html new file mode 100644 index 0000000..0da8cfc --- /dev/null +++ b/blog/802-15-4-wireless/index.html @@ -0,0 +1 @@ +802.15.4 Wireless

802.15.4 Wireless

2023-04-22

'802.15.4' is a low power low-data-rate wireless LAN comminications protocol ideally suited for microcontrollers. And only requires roughly 10-20mA of current to operate (on a 3.3V supply). It has been widely adopted in many applications, such as wireless sensor networks, home automation, and industrial control. It is similar to the widely popular zigbee protocol but is an open standard. Which means it is shipped with opensource RTOS's for platforms that are compatible... My main gripe is that it is yet not popular among hobyists who tend to reach for WiFi in place of this low power network even in microcontroller settings...

The Nordic seires of microcontrollers well support this protocol along with BLE. Compared to BLE this is more suited for industrial noisy environments!

The IEEE 802.15.4 standard defines the physical layer (PHY) and medium access control (MAC) layer for LR-WPANs(low-rate wireless personal area networks). The PHY layer specifies the radio transmission characteristics, including the modulation scheme, data rate, and frequency band. The MAC layer provides mechanisms for channel access, data framing, and error control.

The IEEE 802.15.4 standard supports two frequency bands: 2.4 GHz and 868/915 MHz. The 2.4 GHz band is used in most LR-WPAN applications and provides a data rate of 250 kbps. The 868/915 MHz band is used in some applications that require longer range but lower data rates.

The MAC layer of IEEE 802.15.4 uses a beacon-enabled mode and a non-beacon-enabled mode. In the beacon-enabled mode, a coordinator device periodically transmits a beacon frame that contains information about the network. In the non-beacon-enabled mode, devices can transmit data at any time, without waiting for a beacon frame.

IEEE 802.15.4 also supports two types of topologies: star and peer-to-peer. In the star topology, all devices communicate with a central coordinator device. In the peer-to-peer topology, devices can communicate directly with each other.

In future articles we will see this protocol in action with the Micro:bitV2 which is equipped with the Nordic nRF52833.

\ No newline at end of file diff --git a/blog/aliexpress-intro/index.html b/blog/aliexpress-intro/index.html new file mode 100644 index 0000000..361793d --- /dev/null +++ b/blog/aliexpress-intro/index.html @@ -0,0 +1 @@ +Introduction to AliExpress for the DIY Enthusiast

Introduction to AliExpress for the DIY Enthusiast

2023-11-10

Hey there, DIY enthusiasts and crafty creators! If you’ve got a knack for tinkering and love a good deal, let me introduce you to your next favorite haunt: AliExpress. Items are sourced directly from Chinese sellers, and for us Makers this means they originate from Shenzen more often than not. Another contrast with ebay is that all the listings are buy it now only.

What's So Great About AliExpress for DIYers?

AliExpress is like an infinite toolbox for the DIY enthusiast. It’s crammed with components, from resistors to robotics, at prices that won’t have you eating ramen for a month (unless that’s your project). Need a tiny screw for your drone? A vintage-looking LED for that retro radio? AliExpress has it all, and then some.

Navigating AliExpress

AliExpress's interface is pretty straightforward. Start with a search for your desired component or tool, and then dive into the rabbit hole of options. Use filters to narrow down to the specifics like price range, free shipping, and more.

Tips and Precautions for the Savvy Shopper

  1. Read Reviews and Ratings: These are your compass in the vast sea of products. They can guide you to quality finds and steer you clear of duds.

  2. Check Seller Credentials: Look for sellers with high ratings and a long history of transactions. They’re generally the ones who have proven their reliability.

  3. Beware of Too-Good-To-Be-True Deals: If it looks like a steal, it might just be one—literally. So if you find a Raspberry Pi for the price of a real pie, think twice.

  4. Understand the Specs: Make sure you understand the specifications of what you're buying. That servo motor might look like it fits your needs, but double-check the torque and speed ratings.

The Long Wait: Shipping Timelines on AliExpress

Here’s the kicker: patience is not just a virtue on AliExpress; it’s a necessity. Shipping can take anywhere from a few weeks to a couple of months. So, if you're planning to build that LED cube for a party next week, you might want to look locally instead.

Conclusion: Is AliExpress Worth It for DIYers?

In a nutshell, absolutely! AliExpress is a DIY haven. Yes, you’ll wait longer for shipping, and yes, you need to do your homework on what you’re buying. But the variety is unbeatable, the prices are right, and the sense of community in the review sections can be genuinely heartwarming. So, go forth and create – AliExpress has your back.

So, get those creative gears turning and start browsing! With a bit of savvy shopping and some strategic planning, AliExpress can be a goldmine for the DIY electronics hobbyist. Happy inventing!

\ No newline at end of file diff --git a/blog/analog-filters-primer/index.html b/blog/analog-filters-primer/index.html new file mode 100644 index 0000000..99016da --- /dev/null +++ b/blog/analog-filters-primer/index.html @@ -0,0 +1 @@ +Analog Filters Primer

Analog Filters Primer

2022-02-27

Noise and interference is inherent in any electrical/electronic system. Their effects are especially prominent in analog systems. Most if not all embedded systems are connected to these analog signal lines and fall prey to this problem. Increasingly we find ready made modules that have digitised interface to the analog sensor. But there are times when we struck upon a novel problem and have to deal it ourselves.

Among other things analog filters help mitigate this problem. It is very often the case that this falls in scope of the task of an embedded/firmware developer due to being fairly connected.

Here we see the available resources to help build know-how of analog filtering. The following is a list of copy-left books and resources in sequential progressive order:

DC Electrical Circuit Analysis

AC Electrical Circuit Analysis

Semiconductor Devices

Operational Amplifiers & Linear Integrated Circuits

You may use simulation on PC to experiment and follow along with the above texts. Kicad has a simulation mode which can be used after entering the schematic in eeschema. Look here: http://ngspice.sourceforge.net/ngspice-eeschema.html

Also a prerequisite to above circuit analysis is some basic highschool math: calculus and complex numbers. Refer:

Calculus: Early Transcendentals

A First Course in Electrical and Computer Engineering

\ No newline at end of file diff --git a/blog/basic-app-structure/index.html b/blog/basic-app-structure/index.html new file mode 100644 index 0000000..72f5e06 --- /dev/null +++ b/blog/basic-app-structure/index.html @@ -0,0 +1,98 @@ +Basic application structure

Basic application structure

2022-02-22

The bl_iot_sdk is the development toolkit provided for the development of applications for the BL70X and BL60X controllers. It is quite a pleasant environment built upon the following major components:

  • freeRTOS kernel
  • lwIP stack
  • 'HOSAL' API (based off AliOS-Things HAL)

To acquire the SDK, use the following git command (preferably from the home folder):

git clone https://github.com/bouffalolab/bl_iot_sdk.git
+

ProTip

The sdk docs are available at https://bouffalolab.github.io/bl_iot_sdk/index.html. Use the firefox translate plugin to view it in English. I found that the chrome version is a bit finnicky...


A basic bl-iot-sdk project structure is as follows:

.
+├── compile_commands.json
+├── main
+│   ├── bouffalo.mk
+│   └── main.c
+├── Makefile
+└── proj_config.mk
+

One file may already be familiar to you: compile_commands.json. If not see this post on how to setup a dev environment.

main.c is the main application C file. It contains the main() function. In our blinky example: {% highlight c linenos %} #include <stdio.h> #include <hosal_gpio.h> #include <FreeRTOS.h> #include <task.h>

static hosal_gpio_dev_t gp1;

void main (void) { // setup gp1.port = 0; // <== make sure led connected to pin D0! gp1.config = OUTPUT_OPEN_DRAIN_NO_PULL; hosal_gpio_init(&gp1); hosal_gpio_output_set(&gp1, 1);

uint8_t value = 1;

while (1) { hosal_gpio_output_set(&gp1, value ); value = !value; vTaskDelay(500); } }
{% endhighlight %} The specifics of this file are not important to us right now. We will study the contents in the next post.

The other three makefiles are simply copied over from the official blinky sample, and are required to successfully build the project.

The main makefile Makefile in the project root, is slightly modified:

#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := main
+BL60X_SDK_PATH=~/bl_iot_sdk
+CONFIG_CHIP_NAME=BL702
+export BL60X_SDK_PATH CONFIG_CHIP_NAME
+PROJECT_PATH := $(abspath .)
+PROJECT_BOARD := evb
+export PROJECT_PATH PROJECT_BOARD
+#CONFIG_TOOLPREFIX :=
+
+-include ./proj_config.mk
+
+ifeq ($(origin BL60X_SDK_PATH), undefined)
+BL60X_SDK_PATH_GUESS ?= $(shell pwd)
+BL60X_SDK_PATH ?= $(BL60X_SDK_PATH_GUESS)/../../..
+$(info ****** Please SET BL60X_SDK_PATH ******)
+$(info ****** Trying SDK PATH [$(BL60X_SDK_PATH)])
+endif
+
+COMPONENTS_BLSYS   := bltime blfdt blmtd bloop loopset looprt
+COMPONENTS_VFS     := romfs
+
+SOC_DRV = $(shell echo $(CONFIG_CHIP_NAME) | tr A-Z a-z)
+
+
+INCLUDE_COMPONENTS += $(SOC_DRV)_rf
+INCLUDE_COMPONENTS += $(SOC_DRV)_freertos
+
+INCLUDE_COMPONENTS += $(SOC_DRV) $(SOC_DRV)_std
+INCLUDE_COMPONENTS += hosal mbedtls_lts lwip cli vfs yloop utils blog blog_testc newlibc
+INCLUDE_COMPONENTS += $(COMPONENTS_NETWORK)
+INCLUDE_COMPONENTS += $(COMPONENTS_BLSYS)
+INCLUDE_COMPONENTS += $(COMPONENTS_VFS)
+INCLUDE_COMPONENTS += $(PROJECT_NAME)
+
+include $(BL60X_SDK_PATH)/make_scripts_riscv/project.mk
+

We have hardcoded the chip name and the sdk path. This makes the make command simpler. Also note that we have changed the PROJECT_NAME to a generic name: main. This avoids the need to modify the variable to match each project.

A quick tutorial on Make is viewable here.

NOTE: The makefile variable BL60X_SDK_PATH assumes that you have cloned the sdk into your home directory. If not please modify this variable to reflect your chosen path.

In proj_config.mk:


+####
+CONFIG_SYS_VFS_ENABLE:=1
+CONFIG_SYS_VFS_UART_ENABLE:=1
+CONFIG_SYS_AOS_CLI_ENABLE:=1
+CONFIG_SYS_AOS_LOOP_ENABLE:=1
+CONFIG_SYS_BLOG_ENABLE:=1
+CONFIG_SYS_DMA_ENABLE:=1
+CONFIG_SYS_USER_VFS_ROMFS_ENABLE:=0
+CONFIG_SYS_APP_TASK_STACK_SIZE:=4096
+CONFIG_SYS_APP_TASK_PRIORITY:=15
+
+
+CONFIG_SYS_COMMON_MAIN_ENABLE:=1
+CONFIG_BL702_USE_ROM_DRIVER:=1
+CONFIG_BUILD_ROM_CODE := 1
+CONFIG_USE_XTAL32K:=1
+
+
+LOG_ENABLED_COMPONENTS:= blog_testc hosal
+

In bouffalo.mk:

#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
+
+include $(BL60X_SDK_PATH)/components/network/ble/ble_common.mk
+
+ifeq ($(CONFIG_ENABLE_PSM_RAM),1)
+CPPFLAGS += -DCONF_USER_ENABLE_PSRAM
+endif
+
+ifeq ($(CONFIG_ENABLE_CAMERA),1)
+CPPFLAGS += -DCONF_USER_ENABLE_CAMERA
+endif
+
+ifeq ($(CONFIG_ENABLE_BLSYNC),1)
+CPPFLAGS += -DCONF_USER_ENABLE_BLSYNC
+endif
+
+ifeq ($(CONFIG_ENABLE_VFS_SPI),1)
+CPPFLAGS += -DCONF_USER_ENABLE_VFS_SPI
+endif
+
+ifeq ($(CONFIG_ENABLE_VFS_ROMFS),1)
+CPPFLAGS += -DCONF_USER_ENABLE_VFS_ROMFS
+endif
+
+CPPFLAGS += -DCONF_USER_BL702
+

As a convenience you may simply download the following project skeleton. Extract it to a 'workspace' directory of choice; then whenever you want to initiate a new project, simply run: cp -a src/ my_proj/

Once the above files are in place all you have to do is run:

make  -j
+

Once your project compiles successfully; you may run make flash to burn the firmware (ensuring you have the chip in bootloader mode).

If you ever run into problems run make clean as the first step.

\ No newline at end of file diff --git a/blog/basic-gpio/index.html b/blog/basic-gpio/index.html new file mode 100644 index 0000000..611330c --- /dev/null +++ b/blog/basic-gpio/index.html @@ -0,0 +1,58 @@ +Basic GPIO BL702

Basic GPIO BL702

2022-02-23

The BL702 chip has 15 GPIOs. Each pin can be selected as one of the following modes:

  • ANALOG_MODE

    Used as a function pin, input and output analog.

  • INPUT_PULL_UP

    Input with an internal pull-up resistor - use with devices that actively drive the signal low - e.g. button connected to ground.

  • INPUT_PULL_DOWN

    Input with an internal pull-down resistor - use with devices that actively drive the signal high - e.g. button connected to a power rail.

  • INPUT_HIGH_IMPEDANCE

    Input - must always be driven, either actively or by an external pullup resistor.

  • OUTPUT_PUSH_PULL

    Output actively driven high and actively driven low - must not be connected to other active outputs - e.g. LED output.

  • OUTPUT_OPEN_DRAIN_NO_PULL

    Output actively driven low but is high-impedance when set high - can be connected to other open-drain/open-collector outputs. Needs an external pull-up resistor.

  • OUTPUT_OPEN_DRAIN_PULL_UP

    Output actively driven low and is pulled high with an internal resistor when set high - can be connected to other open-drain/open-collector outputs.

  • OUTPUT_OPEN_DRAIN_AF

    Alternate Function Open Drain Mode.

  • OUTPUT_PUSH_PULL_AF

    Alternate Function Push Pull Mode.

Using GPIO#

blinky

The relevant functions are defined in hosal_gpio.h. Include this header before using any GPIO functions. An example application is as follows:

#include <stdio.h>
+#include <hosal_gpio.h>
+#include <FreeRTOS.h>
+#include <task.h>
+
+static hosal_gpio_dev_t gp1;
+
+void main (void)
+{
+   // setup
+   gp1.port = 0; // <== make sure led connected to pin D0!
+   gp1.config = OUTPUT_OPEN_DRAIN_NO_PULL;
+   hosal_gpio_init(&gp1);
+   hosal_gpio_output_set(&gp1, 1);
+
+   uint8_t value = 1;
+
+   while (1) {
+      hosal_gpio_output_set(&gp1, value );
+      value = !value;
+      vTaskDelay(500);
+   }
+}
+
+

Before we could use the GPIO API, the GPIO struct must be defined and assigned as in line6. Here we're naming it as gp1 to reference later.

Then we must initialise and config a GPIO pin before we can use it. This tas is done in lines 11-13.

The possible value for the 'config' setting was introduced in the start of this article; see above. Finally call the function hosal_gpio_init. Now we can use the API to control them.

As basic functionality of GPIOs, we set the output levels using the following commands:

hosal_gpio_output_set(hosal_gpio_dev_t *gpio, uint8_t value)
+

Value 0 sets the pin in logical 0 / inactive state. Value 1 sets the pin in logical 1 / active state.

To read an input:

hosal_gpio_input_get(hosal_gpio_dev_t *gpio, uint8_t *value)
+

I've looked for a GPIO 'toggle' function to simplify this example, but unfortunately the API docs do not indicate any...

Interrupt Mode#

We look into interrupts using GPIO pins. Each GPIO can be set as an interrupt function. The main idea is to register a callback functionusing the Interrupt API, and define it's contents. As follows:

#include <stdio.h>
+#include <hosal_gpio.h>
+#include <FreeRTOS.h>
+#include <task.h>
+
+static hosal_gpio_dev_t gp1;
+static hosal_gpio_dev_t key1;
+uint8_t value = 1;
+
+void key1_irq(void *arg)
+{
+   hosal_gpio_output_set(&gp1, value );
+   value = !value;
+}
+
+void main (void)
+{
+   // setup
+   key1.port = 8; // the d8 button; note that it's active LOW
+   key1.config = INPUT_HIGH_IMPEDANCE; // button includes external pullup
+   hosal_gpio_init(&key1);
+   hosal_gpio_irq_set(&key1, HOSAL_IRQ_TRIG_NEG_PULSE, key1_irq, NULL); // 'key1_irq' indicates the desired procedure at interrupt.
+
+   gp1.port = 5;
+   gp1.config = OUTPUT_OPEN_DRAIN_PULL_UP;
+   hosal_gpio_init(&gp1);
+   hosal_gpio_output_set(&gp1, 1);
+
+   while (1) {
+   }
+}
+

Line 10 defines the callback function named key1_irq. Here we are simply toggling the D8 LED.

Initialising the Interrupt requires a string of statements beginning on line 19. Notably we initialise it as an input Pin as we covered in the previous section. Then finally use the 'irq-set' function to configure it and indicate our callback function. Note that we had to define the device on line 7.

This is a very basic demonstration of push button with no debounce mechanism. The led may inadvertently toggle multiple times for a single push.

ALT Function Mode#

This is the secondary mode of each GPIO PIN where we tie it to the required peripheral. We'll look at setting up the PINs for alternate function as we deal with the relevant peripheral.

\ No newline at end of file diff --git a/blog/begin-embedded/index.html b/blog/begin-embedded/index.html new file mode 100644 index 0000000..e1fab9f --- /dev/null +++ b/blog/begin-embedded/index.html @@ -0,0 +1 @@ +Best microcontroller for Beginners

Best microcontroller for Beginners

2023-11-05

One of the most asked questions on r/embedded Reddit is "How do I get started in embedded electronics?" or "What is the best microcontroller for beginners?". This post will aim to address that.

If you've seen my About page in this blog, I've discussed a possible set of microcontrollers to keep in his/her skillset as a hobbyiest. Here we will elaborate.

There are wide range of microcontrollers, here are some popular microcontrollers that are great choices for beginners:

  1. Arduino: Arduino is widely considered one of the best options for beginners. It offers a user-friendly, open-source platform with a simple programming environment. The Arduino community is large and supportive, and there are numerous tutorials and projects available online. The Arduino Uno and Arduino Nano are excellent starter boards.

  2. Raspberry Pi Pico: The Raspberry Pi Pico is a microcontroller board from the Raspberry Pi Foundation. It's based on the RP2040 microcontroller and is excellent for beginners. It supports the MicroPython programming language, which is easy to learn and use. The Pico is affordable and has plenty of online resources.

  3. Mbed platform: Then there is also the Mbed platform, developed by Arm, a solid choice for those who want to get into more professional-grade microcontroller programming. Mbed provides a free online IDE and supports a wide range of development boards.

  4. Micro:bit: The BBC Micro:bit is designed for education and is an excellent choice for beginners, especially in a classroom setting. It has a user-friendly block-based programming environment but can also be programmed in Python and JavaScript. It's ideal for teaching programming and electronics concepts.

Many take the Arduino route. Although this may be a good idea; in this blog we're not concerned with Arduino; these are heavy frameworks. Simple, plain and close to metal frameworks are more performant and more importantly - exciting.

Hence my suggestion is to begin with the widely available and inexpensive blue-pill board. It is powered by the ST's ARM Cortex-M3 stm32f103c8; A chip that is widely used in the industry. It's got enough RAM and flash to run your initial projects while you pick up some embedded skills.

Pairing it up with this book: Beginning STM32, will give you a great start with step-by-step hand-holding. This book uses the libopenCM3 HAL (that's Hardware Abstraction Layer) framework. In my opinion this framework is far superior to the ST's default cube HAL which is a botched mess.

Begining STM32 cover

To complement your microcontroller board and to do projects, you will need a set of commonly used electronic components. Fortunately one may find many electronic starter-kits on AliExpress that will suffice for a beginner hobbyist.

Finally you need a multimeter. Again Ali has you covered here. Note that the accuracy of the multimeter is denoted in counts. 4000 counts would probably be about right for hobbyist use; but the more the better.. See here for an explanation. See here for a breakdown of various options.

This post works through building an economical home lab.

\ No newline at end of file diff --git a/blog/bsp-config/index.html b/blog/bsp-config/index.html new file mode 100644 index 0000000..5ad536f --- /dev/null +++ b/blog/bsp-config/index.html @@ -0,0 +1 @@ +Board Setup Config

Board Setup Config

2022-03-15

The board setup configuration is included in the path components/platform/soc/bl702/bl702_std/BSP_Board. In our case it's the bl702_evb sub directory. It houses three files:

  • clock_config.h
  • peripheral_config.h
  • pinmux_config.h

The clock_config.h can be used as-is for our board XT-ZB1 without any changes.

The complimentary online GUI Tool is handy for new users to intuitively generate these config files according to desired custom configurations.

< TODO >

Find out:

  • how to over-ride these files locally per project
  • need to manually edit the pinmux_config.h to accomodate an enabled peripheral (eg SPI)?
\ No newline at end of file diff --git a/blog/building-lab/index.html b/blog/building-lab/index.html new file mode 100644 index 0000000..e9c913a --- /dev/null +++ b/blog/building-lab/index.html @@ -0,0 +1 @@ +Building an affordable electronics lab for hobbyists

Building an affordable electronics lab for hobbyists

2023-11-09

Hey fellow tinkerers and circuit wizards!

In this post, we're diving into one of the most exhilarating journeys you can embark on from the comfort of your own home — building your very own electronics lab! And guess what? You don't need a treasure chest to set it up. With a few savvy picks from AliExpress.com, you can get the essentials without breaking the bank. Let's get charged up and start this electrifying adventure!

example project image

Laying the Foundation with an Electronics Starter Kit

First up, the heart of your lab: an electronics starter kit. This isn't just a box of components; it's the seed from which countless projects will grow. AliExpress has a plethora of options, but look for a kit that's brimming with resistors, capacitors, LEDs, and transistors. Don't skimp on variety here — the more components, the merrier. Make sure it includes a breadboard, jumper wires, and maybe even a small project book to spark your creativity.

The Multimeter: Swiss Army Knife of Embedded Hobbyists

We evaluated various options in this post.

The Power to Innovate: Basic Lab Power Supply

Next, we need a trusty sidekick: a basic lab power supply. You don't need a high-end model for starter projects. A simple adjustable power supply with a range of 0-30 volts and 0-5 amps should suffice. This will let you power almost any project you dream up. Some supplies come with nifty features like current limiting, short circuit protection, and digital displays — these are nice-to-have features that can prevent your projects from going up in smoke.

Crucial Beats: The Cheap Signal Generator

Now, let's talk about setting the tempo with a signal generator. For beginners and budget-conscious builders, a simple function generator from AliExpress will do the trick. Look for one that can dish out square, sine, and triangular waves. Frequency range is key; make sure it can go up to at least 1MHz. This little gizmo is like the metronome for your circuits, essential for testing filters, amplifiers, and so much more.

Microcontrollers: The Brains of the Operation

Microcontrollers are where things get really juicy. They're the brains of your lab, the tiny maestros conducting your electronic symphonies. You don't need the latest and greatest; a few noteworthy contenders can be had for a song. The classics like Arduino Uno or Nano are always a good start (if you go the Arduino route)— user-friendly for beginners and versatile enough for advanced projects. But in this blog we aren't inclined towards Arduino.. See this post for further breakdown.

Storing Your Electronic Treasures

Organization is key to a functioning lab. You can snag component organizers from AliExpress for less than the cost of a fancy coffee. Keep those resistors, capacitors, and tiny ICs in check, and you'll save yourself from the headache of hunting for a 10k resistor in a mountain of electronic confetti.

Soldering: The Alchemy of Electronics

No lab is complete without a decent soldering iron. You don't need a station that could solder a satellite; a basic adjustable temperature iron will serve you well. And a tip on tips: always use a chisel tip; no matter how small the component. Fine tips are not useful since they don't act as a good heat reservoir. For SOIC/SMD soldering lookup on drag soldering. And dont forget the flux.

Illumination: Because You Can't Solder What You Can't See

Don't forget about lighting! A well-lit workspace is crucial. AliExpress offers a variety of LED desk lamps that won't cast a shadow over your budget. Consider one with a magnifying glass; it's like having an eagle-eyed assistant.

Learning Resources: Knowledge is Power

Lastly, the internet is an ocean of resources, but a couple of well-chosen books can be your anchors. Get on Amazon, look for beginner's guides or project books that resonate with your interests, whether it's robotics, home automation, or something else that gets your electrons excited. A few free copy-left books to learn circuit theory was discussed here.

Safety First!

Before I sign off, a word to the wise: safety is paramount. Always double-check connections before powering up, never work on a live circuit, and invest in a pair of safety goggles. Your future self will thank you.

Building an electronics lab is like piecing together your very own technical wonderland. It's a place where imagination meets reality, where learning meets doing, and where the only limit is the scope of your curiosity. And the best part? It doesn't have to cost an arm and a leg. With these tips and AliExpress as your marketplace, you're all set to wire up your world.

Stay charged, my friends, and happy building!

\ No newline at end of file diff --git a/blog/c-linkerscripts/index.html b/blog/c-linkerscripts/index.html new file mode 100644 index 0000000..c1372b1 --- /dev/null +++ b/blog/c-linkerscripts/index.html @@ -0,0 +1,2 @@ +C linker scripts howto

C linker scripts howto

2022-11-16

In this article series we will look at dealing and understanding linker script writing. Targetting the RISC-V RV32 core. We begin with looking at programming assembly, as this will help understand the innards of the microcontroller.

The RISC-V chip was intended and intentionally designed as a heuristic use case, coming in from academic origins has now found popularity among the commercial setting. As such it is ideal microcontroller for tackling to understand the assembly environment, and given it's rising populatity among the other giants in the field especially ARM cortex; has practical value.

You may ofcourse run a simulation by installing the toolchain and qemu along with gdb; which then enables the ability to run in step and inspect the memory state. However for a total beginner an all inclusive ready-built package is more useful. Ripes is such a package. It comes as an appimage so just download and execute from the terminal to fire it up!

One major advantage of Ripes is that it contains simulation of external peripherals such as LED matrix and switches. Which makes it more engaging for the new-commer.

Ripes in action

Stay tuned..

\ No newline at end of file diff --git a/blog/debian-testing/index.html b/blog/debian-testing/index.html new file mode 100644 index 0000000..71cfc30 --- /dev/null +++ b/blog/debian-testing/index.html @@ -0,0 +1 @@ +My experience with Debian Testing

My experience with Debian Testing

2023-05-11

I'm a big proponent of Linux [Or GNU/Linux to be technically correct]. I like the idea of how the entire system is just a collection of individual functioning pieces of software.

Their motto is Do one thing and do it well. I think this is a great mantra for successful engineering altogether. That is, break down the task into simple manageable tasks and implement and integrate them!

Although it's not as popular in desktops and personal computer markets, it unanimously powers the web. A good portion of servers run linux. This is no surprise considering the reliability and flexibility of the Linux OS. To probe further, the popularity of the humble Wordpress platform drives the need for Linux web servers, as Wordpress is run on top the old school LAMP stack (Where L denotes the Linux part).

Among Linux servers, Debian is one of the most popular1. The mainline Debian distribution is considered to be the hallmark of stability. That's great. But it comes with a downside... It's software repository is not the most upto-date. Usually you'd be pulling in updates that are lagging around two years behind the current status. So it seems Debian is not so great for powering a daily driver personal workstation.

Enter Debian Testing. Debian has two other stable stuatus of releases: Unstable and Testing. Unstable was too unstable for me. Testing seemed like a good middle-ground. You can read more about Testing here. One of the pre-requisites is that the package to make it into Testing must have been in Unstable for atleast two days.

One major problem with these Unstable/Testing releases is that they tend to unexpectedly break software when updating. But there are ways to fortify testing. We do this with an application called Timeshift. As the name implies, we can backup the hard drive contents and revert the system to a previous state following an undesired outcome.

Note that Timeshift requires the partition to be formatted in btrfs type and that too in the right naming scheme. The following Youtube video has instructions on installing Debian Testing this proper way with Timeshift enabled if you want to try it out.

In my experience it wasn't good enough. I found myself reaching too often to the Timeshift backups, from things breaking after an update. Very often I had the problem of installed applications going missing.

Hence I just use POP-OS. It comes from the Debian lineage, so we have familarity there especially with the package manager apt. It's a very popular desktop based distribution. However I'm not found of it's Cosmic Desktop. I find it a butchered up version of GNOME desktop. To install the GNOME desktop I just have to run sudo apt install gnome-session. I run trusty Debian for servers and VPSs (like we did in the other post about Servarr as a Docker host!).

\ No newline at end of file diff --git a/blog/debug-bl702/index.html b/blog/debug-bl702/index.html new file mode 100644 index 0000000..aa31454 --- /dev/null +++ b/blog/debug-bl702/index.html @@ -0,0 +1,86 @@ +Debugging BL702

Debugging BL702

2022-03-17

You may use the advertised Sipeed RV-Debugger Plus. It emulates a dual device one for the flashing (UART) and for JTAG.

Then hookup the probe according to the following pinouts on the BL702:
BL702 PinJTAG Function
D0TMS
D1TDI
D2TCK
D9TDO

You may ofcourse simply plug it with breadboard dupont wires. But I found it convenient to use one of these 2x5 IDC ribbon cables:

IDC ribbon cable

and solder them on straight to the board like this:

soldered board

Once the hardware is setup, place the following files in the ~/openocd-cfg/ directory:

In if_rv_dbg_plus.cfg:

# BouffaloLab USB-JTAG/TTL adapter
+adapter driver ftdi
+ftdi_vid_pid 0x0403 0x6010
+
+ftdi_channel 0
+#ftdi_tdo_sample_edge falling
+transport select jtag
+adapter speed     4000
+
+ftdi_layout_init 0x00f8 0x00fb
+#ftdi_layout_signal nTRST -data 0x0400
+#ftdi_layout_signal nSRST -ndata 0x0020
+
+#reset_config srst_only srst_push_pull
+#adapter_nsrst_delay 100
+#adapter_nsrst_assert_width 100
+

In tgt_702.cfg:

#target chip
+
+set _CHIPNAME riscv
+jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x20000e05
+
+set _TARGETNAME $_CHIPNAME.cpu
+target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME
+$_TARGETNAME.0 configure -work-area-phys 0x22020000 -work-area-size 0x10000 -work-area-backup 1
+#$_TARGETNAME.0 configure -rtos auto
+
+echo "Ready for Remote Connections"
+
+$_TARGETNAME.0 configure -event reset-assert-pre {
+    echo "reset-assert-pre"
+    adapter speed 100
+}
+
+$_TARGETNAME.0 configure -event reset-deassert-post {
+    echo "reset-deassert-post"
+    adapter speed 4000
+    reg mstatus 0x7800
+    reg mie 0x0
+#    reg pc 0x22008000
+}
+
+$_TARGETNAME.0 configure -event reset-init {
+    echo "reset-init"
+# 4MHz for FPGA
+    adapter speed 4000
+}
+
+gdb_memory_map enable
+gdb_flash_program disable
+
+riscv set_prefer_sba on
+riscv set_command_timeout_sec 1
+
+init
+reset init
+
+#jtag arp_init
+
+#resume
+#exit
+

In 702.init:

set architecture riscv:rv32
+#target remote :3333
+#set disassemble-next-line on
+set mem inaccessible-by-default off
+#gdb_breakpoint_override [hard|soft|disable]
+
+mem 0x22008000 0x22014000 rw
+mem 0x42008000 0x42014000 rw
+mem 0x22014000 0x22020000 rw
+mem 0x42014000 0x42020000 rw
+mem 0x22020000 0x22030000 rw
+mem 0x42020000 0x42030000 rw
+mem 0x22030000 0x2204C000 rw
+mem 0x42030000 0x4204C000 rw
+mem 0x23000000 0x23400000 ro
+

Then run:

openocd -f ~/openocd-cfg/if_rv_dbg_plus.cfg -f ~/openocd-cfg/tgt_702.cfg
+

Hopefully you'll get the following message:

[riscv.cpu.0] Target successfully examined.
+

Now that openocd sucessfully connects to the target board, next step is to fire-up gdb:

~/bl_iot_sdk/toolchain/riscv/Linux/bin/riscv64-unknown-elf-gdb <path_to_project>/build_out/main.elf -x ~/openocd-cfg/702.init
+

Now you can finally attach a GDB session like this on the (gdb) prompt:

target extended-remote localhost:3333
+

Further, considering that the dev board uses external flash, there are two ways to debug on chip:

  • debug code on ram
  • pre-load code to xip as usual using serial, before starting the debug session.

More details can be found on the official pdf1. We will use the second method, on the (gdb) prompt:

set $pc = 0x21000000
+set $mie = 0
+set $mstatus = 0x1880
+

Now you can step throught the code and probe to your heart's liking. For example:

thb main
+continue
+continue
+

See more commands in the official pdf1. To learn more about GDB see here.

1

https://github.com/bouffalolab/bl_docs/blob/main/BL602_Openocd%26GDB/en/Introduction%20of%20OpenOCD%20and%20GDB.pdf

You can also use a typical ftdi232rl as a debugger probe for openocd as dicussed in this post. But due to conflicts on pins for UART and JTAG mode on the ftdi module, you'll need to setup two modules each for flashing (UART) and probing (JTAG).

\ No newline at end of file diff --git a/blog/dev-env/index.html b/blog/dev-env/index.html new file mode 100644 index 0000000..db70bd4 --- /dev/null +++ b/blog/dev-env/index.html @@ -0,0 +1,6 @@ +Setup a development environment

Setup a development environment

2022-02-22

In this guide we will be setting a development environment for the bl602 development. Neovim is used here but you may choose to your liking. The important requirement is to setup code completion in your editor that'll give ide like features. I find that this is actually useful in the initial phases to aid learning the new framework.

To do this, we are going to setup lsp on neovim. Lsp is native to neovim and only requires some config settings! But you dont have to do this yourself, there's a github repository with preset config file you can just copy. It also helps to configure sane defaults to other settings.

First install neovim. Then you need a language server, kickstarter uses clangd. Install this via your package manager. In ubuntu:

sudo apt install clangd
+

Finally copy the kickstarter config. That's it for installation. You may scout around kickstarter to personalise your neovim setup further.

You also need to install a small app called bear. Then in your project directory run:

make clean
+bear -- make
+

This spits out a file called compile_commands.json in your project root. Now you will have super ide like features in Neovim that make programming much more pleasurable!

clangd would throw out an error: unknown argument: '-fstrict-volatile-bitfields'; to avoid this you can do so by instructing clangd to remove that flag from the compile command. To do this create a file ~/.config/clangd/config.yaml with the following contents:

CompileFlags:
+ Remove: [-fstrict-volatile-bitfields]
+
\ No newline at end of file diff --git a/blog/dev-station/index.html b/blog/dev-station/index.html new file mode 100644 index 0000000..d3597f1 --- /dev/null +++ b/blog/dev-station/index.html @@ -0,0 +1 @@ +Building a Developer's Workstation

Building a Developer's Workstation

2023-05-23

In this article we will see how we build a developer's workstation that has good overall performance and suitably compact in size.

Our first decision is the OS. I may be a little biased here. But you may have guessed that Linux is our choice of OS. As discussed in [this article]({% post_url /update/2023-05-11-debian_testing %}), Debian is unsuitable as a daily driver. We choose it's derivative POP! OS as our OS. It is a quite popular distro that is well maintained.

Hardware wise our goto is the ex-lease HP Elitedesk 800 G2 mini desktop. Although it comes in several revisions, my recommendation is to go for the G2 version (running the 6th Gen core-i5) which I think is a good compromise between cost and performance/efficiency. I believe the G4 revision is the current one as of Q2 2023, which can be purchased new direct from HP site.

The good thing about buying an ex-lease item (other than low cost) is that you repurpose it which would have otherwise ended up straight in a landfill. And you help out the planet which is a win-win. In addition to this, Linux loves these ex-lease systems. Being a few years old and bieng in the field for a while, linux has mature drivers for it's chipsets and peripherals.

These HP/DELL branded office desktops are very durable systems that they pose great value even as second hand ex-lease systems. That's one thing that I observed in my stint as a repair tech at a refurbished computer store.

Here's a referral link to ebay if you are interested to purchase this. This gets me a small commission at no extra cost to you!

Installing Software#

The absolute first thing you must install in a fresh install of POP! OS (or any distro for that matter) is Oh My Zsh. It genuinely is your terminal on steroids. It will spruce up your terminal to a whole another level. Some of it's features include:

  • Predictive Completion
  • Enhanced Tab completion
  • Syntax highlighting (right in the shell!)

This article has a guide on how to install it and a couple plugins to get the above functionality.

Next comes the decision on a text editor. If you'd like to go the vim route, I highly recommend taking a look at [Doom Emacs]({% post_url /update/2022-11-16-doom %}). It's emacs but with vim keybindings. Or you could go with the ever popular VS Code.

Finally you need to install your development environment for the framework that you use, be it web dev (node/PHP/Python etc backend) or for embedded (Zephyr in our case). If you'd like to dive into Zephyr development, take a look of my Zephyr Guide.Nordic dev academy also has a great intro.

Have fun hackin'.

\ No newline at end of file diff --git a/blog/device-drivers-101/index.html b/blog/device-drivers-101/index.html new file mode 100644 index 0000000..2b480e6 --- /dev/null +++ b/blog/device-drivers-101/index.html @@ -0,0 +1 @@ +Device Drivers 101

Device Drivers 101

2022-02-24

The SDK has the provisions for device tree system. However this has been segregated into the flash tool with all the communication ports enabled. We will write our device driver in the conventional way in pure C, built on top of the relevant peripheral API; eg, UART, I2C...

i2c gpio extender

I2C GPIO extender#

To exercise the I2C bus in this post, we’ll be using the PCF8574 GPIO extender chip. This is a great chip for adding additional GPIO lines, provided that you don’t need high speed (the demo operates the I2C bus at ??? kHz). This will be an exercise in writing device drivers; first of many more to come!

\ No newline at end of file diff --git a/blog/diy-jtag-debugger/index.html b/blog/diy-jtag-debugger/index.html new file mode 100644 index 0000000..97bd290 --- /dev/null +++ b/blog/diy-jtag-debugger/index.html @@ -0,0 +1,8 @@ +A DIY jtag debugger

A DIY jtag debugger

2022-02-16

One of openocd's hidden perks is it could use a typical ftdi232rl USB to Serial converter into a makeshift JTAG programmer. Not only is this cheap, it's quick convenience than to wait and order a new programmer.

In this article we see how to put this to use...

First we have to install openocd if you haven't already. This could be done using your systems package manager. Or if you're installing manually be sure to enable the interface when building openocd in the following step:

./configure --enable-ft232r
+

Hookup your board to the ftdi board. Follow the following pinouts:
JTAG Pinftdi Pin
TDIRXD
TCKTXD
TDORTS
TMSCTS
TRSTDTR
SRSTDCD

Here's a pic with the debugger connected to a DT-BL10 (RISC-V) board:

ftdi connection

Then you need to fireup openocd with a config. To do this, create a file called my_ft232r.cfg somewhere in you home directory with the following contents:

adapter driver ft232r
+adapter speed 3000
+ft232r_restore_serial 0x15
+

Now fireup openocd:

openocd -f path/to/my_ft232r.cfg/
+

Hopefully your target is detected!

\ No newline at end of file diff --git a/blog/diy-nas/index.html b/blog/diy-nas/index.html new file mode 100644 index 0000000..bfb079f --- /dev/null +++ b/blog/diy-nas/index.html @@ -0,0 +1,277 @@ +DIY NAS

DIY NAS

2023-05-10

Apart from having a local collection, the main advantage of an automated servarr (sonarr/radarr) setup is the possiblilty of auto-populating a list of titles based on a certain criteria. For example: all 'Marvel' films. In this article we will see how to build such a setup.

I think running a DIY NAS is a great hobby. This is an excellent exercise in selfhosting linux servers with the aim of maintaining them. And servarr for that matter is reasonably complex to integrate; you'll get a good feel of building docker services that will open the door to many other selfhosted services!

Considerations for our setup#

Arm SBC vs x86 powered machine#

This is the primary decision in our DIY NAS build. While SBC's are more power efficient, the high capable processors are expensive which is a requirement for direct media streaming using plex/emby. The x86 alternative is a cheaper route... Especially if you go with a second hand ex-lease SFF(small form-factor) machine. Which is what I did. These come in a great discounts on ebay seasonally (as low as $50), get one with 8G of memory and atleast a core-i5 processor. As compared to an SBC unit, these alredy come in a sturdy case to safely house your HDDs. (They can take a max of 2 storage HDDs).

You have lots of choices here too... Dell Optiplex 3020 to HP Elitedesk G1/G2 and so on.

-HP Elitedesk 800 G1

-HP Elitedesk 800 G2

-Dell Optiplex 3020

I personally use the first one... (Note that the above are affiliate links to ebay, where I get a small commission if you purchase through them)

OS#

Off the shelf (eg. freeNAS) vs plain Debian. Going with freeNAS we get the advantage of preconfigured easy RAID setup, but for our simple setup we won't use any RAID. Maybe just one or two HDDs (as our case supports) joined with the Logical Volume Manager (LVM) filesystem. The advantage of going with plain Debian in my opnion (other than flexibility) is that you don't have to put up with pesky UIs for tasks that can be easily achieved via the terminal. Be warned though, since we are managing a headless Linux system remotely this will be a lot of terminal commands through SSH session.

Setting up Services#

In my setup I have a 2.5" HDD for the boot Debian drive, and a separate 3.5" 4TB HDD for data storage, which like mentioned above expandable with LVM. This drive will be mounted to /mnt/main (I named the drive as main in the LVM). You will see reference to this drive path below when we configure our services.

Makesure you set an identifiable hostname to your Debian install, and to access it via this name you will have to install avahi-daemon:

sudo apt install avahi-daemon
+

At this point you can detach from keyboard and access you machine remotely... Using SSH: ssh <user>@<your_hostname>.

Next install docker and docker-compose. Using the guides here and here respectively. There's instructions on those guides to setup the relevant repositories for Debian.

DockStarter#

If you are new to Docker services, a great place to start is the aptly named DockStarter. It is an application with settings preconfigured and the Docker commands have been made under-the-hood. You just make selections through the menulist wizard to enable/disable services. And it comes with a vast majority of services too. See here. It does cover most use cases of a Sonarr/Radarr setup!

In my opnion, DockStarter is great to understanding the mechanics of how Docker works without first getting bogged down with configuration details. Once you have understood this concept I encourage you to move on.

Do keep in mind DockStarter is a well polished piece of software, which is still handy for simple one-container deployments... In our case we have a whole host of containers interacting with each other, which is difficult to deploy even with DockStarter.

See here if you want to setup Servarr using DockStarter. This site has good information for all kinds of Servarr installs, so you could always refer here no matter which route you take!

Here we will be looking at plain docker-compose method of setting up services. The first course of action is to prepare our data drive.

Preparing the drive#

First we need to identify the disk ID. Run sudo fdisk -l | grep -i /dev/sd; in my instance for example:

Disk /dev/sdc: 3.64 TiB, 4000787030016 bytes, 7814037168 sectors <=== my 4T drive!
+/dev/sdc1   2048 7814037134 7814035087  3.6T Linux filesystem
+Disk /dev/sdb: 1.82 TiB, 2000398934016 bytes, 3907029168 sectors
+/dev/sdb1   2048 3907029134 3907027087  1.8T Linux filesystem
+Disk /dev/sda: 149.05 GiB, 160041885696 bytes, 312581808 sectors
+/dev/sda1       2048   1050623   1048576   512M EFI System
+/dev/sda2    1050624 310579199 309528576 147.6G Linux filesystem
+/dev/sda3  310579200 312580095   2000896   977M Linux swap
+

We can see above that dev/sdc is our drive! Continue to follow instructions on formatting the drive here.

Finally add this line to /etc/fstab to enable automount:

/dev/vgmain/lvmain      /mnt/main ext4    defaults,user 0 1
+

Note that you will need to create the main directory in /mnt. And note that I named my volume group, vgmain and the logical volume lvmain.

Once we have formatted the drive and have made provisions to auto-mount it; the last step is to create the directory structre that will hold our data.

media
+├── torrents
+│  ├── movies
+│  ├── music
+│  └── tv
+├── usenet
+│  ├── movies
+│  ├── music
+│  └── tv
+└── medialibrary
+   ├── movies
+   ├── music
+   └── tv
+

Run the following commands to create the above structure:

cd /mnt/main
+mkdir media
+cd media
+mkdir -p {torrents,usenet,medialibrary}/{movies,tv}
+

Download source#

Before we proceed, we have one more decision to make, regarding our download method to acquire content. There are three options:

  • Torrent
  • Usenet
  • Debrid

We wont use torrents. Despite it being free, it's a network hog.

Debrid is a service that consolidates numerous hosting services under a single service. Thereby you can download in premium from all supported hoster just by logging into Real-Debrid. They now also have the option of direct downloading a cached torrent source. This is what makes it possible to use it in the servarr setup. To use it you need to enable the services jackett and rdt-client below in the 'downloaders' section.

Here we will use usenet as our download source. More instructions on setting up usenet is presented in [this article]({% post_url /bl/2022-02-23-basic-gpio %}). To use this we have enabled nzbget as our downloader below.

docker-compose config#

Make a new directory called servarr in your home directory to house the docker-compose config. This file will gather the configurations of all the services into one file. Once you have made this file you just have to execute sudo docker compose up -d within this directory in your bash shell. And after a few minutes the services will be accessible from their specified ports from the server's IP address. Here's my docker-compose.yml placed inside of that directory:

version: "2.1"
+services:
+  organizr:
+    image: linuxserver/organizr
+    container_name: organizr
+    environment:
+      - PUID=1000
+      - PGID=1000
+      - TZ=Europe/London
+    volumes:
+      - /mnt/main/config/Organizr:/config
+    ports:
+      - 80:80
+    restart: unless-stopped
+
+  filebrowser:
+    image: hurlenko/filebrowser
+    environment:
+      - PUID=1000
+      - PGID=1000
+      - TZ=Europe/London
+    ports:
+      - 443:8080
+    volumes:
+      - /mnt/main:/data
+      - /mnt/main/config/fb:/config
+    restart: always
+
+  syncthing:
+    image: syncthing/syncthing
+    container_name: syncthing
+    hostname: mediasrv
+    environment:
+      - PUID=1000
+      - PGID=1000
+    volumes:
+      - /mnt/main/media:/var/syncthing
+    ports:
+      - 8384:8384
+      - 22000:22000/tcp
+      - 22000:22000/udp
+    restart: unless-stopped
+    
+    
+    
+  radarr:
+    container_name: radarr
+    image: ghcr.io/hotio/radarr:latest
+    restart: unless-stopped
+    logging:
+      driver: json-file
+    ports:
+      - 7878:7878
+    environment:
+      - PUID=1000
+      - PGID=1000
+      - TZ=Europe/London
+    volumes:
+      - /etc/localtime:/etc/localtime:ro
+      - /mnt/main/config/radarr:/config
+      - /mnt/main/media:/data
+  sonarr:
+    container_name: sonarr
+    image: ghcr.io/hotio/sonarr:latest
+    restart: unless-stopped
+    logging:
+      driver: json-file
+    ports:
+      - 8989:8989
+    environment:
+      - PUID=1000
+      - PGID=1000
+      - TZ=Europe/London
+    volumes:
+      - /etc/localtime:/etc/localtime:ro
+      - /mnt/main/config/sonarr:/config
+      - /mnt/main/media:/data
+
+  lidarr:
+    image: lscr.io/linuxserver/lidarr
+    container_name: lidarr
+    environment:
+      - PUID=1000
+      - PGID=1000
+      - TZ=Europe/London
+    volumes:
+      - /mnt/main/config/lidarr:/config
+      - /mnt/main/media:/data
+    ports:
+      - 8686:8686
+    restart: unless-stopped
+    
+# downloaders  =========================================================================
+
+
+#  jackett:
+#    image: ghcr.io/linuxserver/jackett
+#    container_name: jackett
+#    environment:
+#      - PUID=1000
+#      - PGID=1000
+#      - TZ=Europe/London
+#      - AUTO_UPDATE=true #optional
+#      #- RUN_OPTS=<run options here> #optional
+#    volumes:
+#      - /mnt/main/config/jackett:/config
+#      - /dev/null:/downloads
+#    ports:
+#      - 9117:9117
+#    restart: unless-stopped
+#
+#  rdtclient:
+#    container_name: rdtclient
+#    environment:
+#      - PUID=1000
+#      - PGID=1000
+#      - TZ=Europe/London
+#    volumes:
+#        - '/mnt/main/media/torrents/downloads:/data/downloads'
+#        - '/mnt/main/config/rdt-client/:/data/db'
+#    image: rogerfar/rdtclient
+#    restart: always
+#    logging:
+#        driver: json-file
+#        options:
+#            max-size: 10m
+#    ports:
+#        - '6500:6500'
+      #
+      #  bittorrent:
+      #    image: ghcr.io/linuxserver/qbittorrent
+      #    container_name: qbittorrent
+      #    environment:
+      #      - PUID=1000
+      #      - PGID=1000
+      #      - TZ=Europe/London
+      #      - WEBUI_PORT=8080
+      #    volumes:
+      #      - /mnt/main/config/qbittorrent:/config
+      #      - /mnt/main/media/torrents:/data/torrents:rw
+      #    ports:
+      #      - 6881:6881
+      #      - 6881:6881/udp
+      #      - 8080:8080
+      #    restart: unless-stopped
+      #
+  nzbget:
+    container_name: nzbget
+    image: ghcr.io/linuxserver/nzbget
+    restart: unless-stopped
+    logging:
+      driver: json-file
+    ports:
+      - 6789:6789
+    environment:
+      - PUID=1000
+      - PGID=1000
+      - TZ=Europe/London
+    volumes:
+      - /etc/localtime:/etc/localtime:ro
+      - /mnt/main/config/nzbget:/config
+      - /mnt/main/media/usenet:/data/usenet:rw
+
+
+networks:
+  default:
+    external:
+      name: mynet
+

Note the ports. You need them to access the individual services. For example to access Radarr point to http://<your_server>:7878. On the default port (80) we have configured Organizr. Which is a bookmarker to other services to easily access them. Read more about this here. Note that in the config above, I have added filebrowser and syncthing services in case they become handy.

Configuring Downloader#

See above in Download source for more information on this.

Configuring Radarr#

See the separate post [Configuring Radarr]({% post_url /bl/2022-02-23-basic-gpio %}) on how to do this. Trash Guides is very handy.

Repeat the same steps with Sonarr as well to configure it.

Media Streamer#

The one thing missing in the above config is the media streamer app. This is the service that let's you directly play content from your browser! Due to it's independant nature from the other apps I decided to keep this in a separate file. So we can easily bring up or down our servarr services together independent of the streaming service.

You can choose from three vendors:

  • Plex
  • Emby
  • Jellyfin

We will choose Emby. So in your home folder again create a new directory and name it emby. Inside it put the following file docker-compose.yml:

version: "3.2"
+services:
+  emby:
+      image: emby/embyserver
+      container_name: emby1
+      network_mode: host # Enable DLNA and Wake-on-Lan
+      environment:
+        - UID=1000
+        - GID=1000
+        - TZ=Europe/London
+          #- UMASK_SET=022 #optional
+      volumes:
+        - /mnt/main/config/Emby:/config
+        - /mnt/main/media/medialibrary/tv:/data/tvshows
+        - /mnt/main/media/medialibrary/movies:/data/movies
+        - /mnt/main/media/medialibrary/music:/data/music
+      ports:
+        - 8096:8096
+          #- 8920:8920 #optional for secure connections
+      restart: unless-stopped
+

Setting up Monitoring#

Last but not least we can set up beautiful graphs and charts on server monitoring. We use Graphana with Prometheus to achieve this. Here's the docker-compose.yml for monitoring tools:

# System Monitoring =========================================================================
+
+  prometheus:
+      image: prom/prometheus
+      container_name: prometheus
+      environment:
+        - PUID=1000
+        - PGID=1000
+        - TZ=Europe/London
+        - UMASK_SET=022
+      volumes:
+        - /mnt/main/config/Prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
+      ports:
+        - 9292:9090
+      restart: unless-stopped
+
+  node-exporter:
+      image: quay.io/prometheus/node-exporter:latest
+      container_name: node-exporter
+      network_mode: host
+      environment:
+        - PUID=1000
+        - PGID=1000
+        - TZ=Europe/London
+        - UMASK_SET=022
+      volumes:
+        - /:/host:ro,rslave
+      restart: unless-stopped
+      
+      
+    
+  grafana:
+    image: grafana/grafana
+    container_name: grafana
+    environment:
+      - PUID=1000
+      - PGID=1000
+      - TZ=Europe/London
+      - UMASK_SET=022
+      - GF_SECURITY_ALLOW_EMBEDDING=true
+    ports:
+      - 3000:3000
+    restart: unless-stopped
+
+
+networks:
+  default:
+    external:
+      name: mynet
+

Note that you need to place a file under /mnt/main/config/Prometheus/prometheus.yml with the following contents:

global:
+  scrape_interval: 5s
+  external_labels:
+    monitor: 'node'
+scrape_configs:
+  - job_name: 'prometheus'
+    static_configs:
+      - targets: ['192.168.1.169:9292'] ## IP Address of the localhost
+  - job_name: 'node-exporter'
+    static_configs:
+      - targets: ['192.168.1.169:9100'] ## IP Address of the localhost
+
+

Remember to change the IP address above to match the one of your server! To configure the GUI watch one of the many Youtube videos on "server monitor with graphana and prometheus". Here's a snapshot of my Graphana view:

my grpahana snapshot

\ No newline at end of file diff --git a/blog/doom/index.html b/blog/doom/index.html new file mode 100644 index 0000000..076cbf3 --- /dev/null +++ b/blog/doom/index.html @@ -0,0 +1 @@ +DOOM Emacs intro

DOOM Emacs intro

2022-11-16

I've always been shuffling around neovim, emacs, vscode. This writeup will explain how I've settled on the use of emacs. DOOM Emacs specifically.

doom_logo

It's github page is accessible DOOM Emacs; documentation here and installation for all platform are accessible here.

DOOM Emacs is a batteries included edition of emacs. Together with which-key and easy language enable features (layers), it has a very intuitive experience which is ideal for the beginner (to command line editior altogether!) I think DOOM's got a clean modern funky interface too.

The one thing it's lacking is a newbie friendly guide/intro to using it. In contrast to my opinion; the developers' hold that DOOM is for an experienced emacs user. I beg to differ as stated above.

To help with this user onboarding issue, when I've tried to pick up usage, I've scoured the internet to put together a single sheet1 summarising all the essential commands. Accessible here.

Let me know down what's you favourite newbie friendly emacs edition is?

1

That's all the familiarity it requires. That's how intuitive it is!

\ No newline at end of file diff --git a/blog/drop-ship/index.html b/blog/drop-ship/index.html new file mode 100644 index 0000000..30c5b80 --- /dev/null +++ b/blog/drop-ship/index.html @@ -0,0 +1 @@ +My experience with drop-ship warehousing

My experience with drop-ship warehousing

2023-05-17

I had an idea to pakcage, ship and store these bl702 devkits into a US warehouse ready to locally distribute them. This is the story of how I failed misarably at it. Basically I purchased a lot of 100 pieces of these devkits to be stored into a warehouse to be shipped locally in the US. Although this never translated into sales; it did have many hurdles.

First was in purchasing itself. Basically I couldn't purchase from the ALL FAMILIAR Aliexpress. Because I had to locally ship it into the ligistic company's site in China; I could only ship to an international address. So Aliexpress was out of the question. For Chinese Domestic purchase, the supplier had a store in Taobao. This is fully in Chinese interface. Obviously.

Trying to register on the PC via the web portal did not succeed due to geographical blocks by Taobao. I did manage to register on the mobile app however. But unlike on the browser there's no way to easily translate for example with the Google translate plugin. What I had to do instead painstakingly, point another phone with Google lens!

That's one hurdle overcome. I had to fill in the local logistical company's address and all the details holding up on the other phone.

It took two days for the goods to reach the logistic's warehouse in China. They arrived in a box packaged with the 100 units.

Now the logistics company I went with was called Nextsmartship. My experience was largely positive with them. There was a another caveat here. When using this logistics company, it is way cheaper to prepackage into individual ready-to-mail packages here in the Chinese warehouse than to ship in bulk first and parcel them for courier there. It worked out to be $0.99 per piece here.

So it would be ideal to pre-package them into appropriate package unit sizes before cargo-ing to the US: for example 1pc, 5pc and 10pc units. I also think they did a very good job of safely packaging them into individual pieces.

At the time I dealt with them, they did not have automated integration with 3rd party sites (such as woo-commerce which I used). So once you make a sale on your site you had to manually message them (on Whatsapp). But this never happened in my instance.

As you see, I think one of the requirements of selling products on your own hosted site, to even gain some exposure, you'd have to be listed on Google merchant. This is the list of shopping products you'd get when a buyer clicks on the Shopping tab on the Google search page.

I didn't realise at the time, but you'd need a complete site with "Privacy Policy"/"Returns Policy" etc to get approved in Google Merchant. Google outright dismisses your site for misrepresentation for this. I mis-took it for selling my products that was aimed at the Chinese market... So I never bothered to probe further on this. And the whole thing went down the drain.

However at this point, I myself started to get frustrated with this devkit's SDK: it's lack of documentation and more importantly the everchanging updates to the SDK that breaks backwards compatibility. As I have pointed out earlier I moved on to the Nordic nRF chips. Due to lack of hobbiest sized boards (think bluepill). I put together the 833iot for a self-contained module based off nRF52833 sold in Alibaba. Due to previous failures I never attempted to sell them.

833iot pcb

Even though low-power wireless and Thread are increasingly becoming popular, the hobbiest market is still flooded with the power-hungry WiFi chips. This protocol is so inappropriate for microcontrollers!

\ No newline at end of file diff --git a/blog/esp32c3/index.html b/blog/esp32c3/index.html new file mode 100644 index 0000000..98bfa33 --- /dev/null +++ b/blog/esp32c3/index.html @@ -0,0 +1 @@ +Switching to the ESP32-C3

Switching to the ESP32-C3

2022-03-18

This blog will here-on switch to the esp32-c3 controller...

It is noted that the esp32 chips are infamous for higher power consumption, especially with the radio on. But it's expected that this problem will disappear with the introduction of the upcomming ESP32-H2 MCU.

The rationale for the switch is the incomplete and nonfunctional tooling on the Buffalo Lab boards, especially the inability to use JTAG debugging.

The esp32-c3 is a similarly low cost solution with a mature SDK and built-in debugger.

\ No newline at end of file diff --git a/blog/freertos/index.html b/blog/freertos/index.html new file mode 100644 index 0000000..805179e --- /dev/null +++ b/blog/freertos/index.html @@ -0,0 +1,39 @@ +freeRTOS Overview

freeRTOS Overview

2022-02-24

The SDK includes the freeRTOS as the system kernel. More info can be seen by clicking on the 'More Info' button on the top of this page. But the kernel is already configured for use in the SDK, so we do not need to worry about it's config or setting up.

In this article we'll go through a rough overview of freeRTOS of just the necessary features.

2tasks

Tasks#


+#include <hosal_gpio.h>
+#include <FreeRTOS.h>
+#include <task.h>
+
+static hosal_gpio_dev_t gp1, gp2;
+
+void my_task (void *p)
+{
+   static _Bool val = 1;
+
+   while (1) {
+      hosal_gpio_output_set(&gp2, val);
+      val = !val;
+      vTaskDelay(pdMS_TO_TICKS(700));
+   }
+}
+
+void main (void)
+{
+   // setup
+   gp1.port = 5;
+   gp1.config = OUTPUT_OPEN_DRAIN_NO_PULL;
+   hosal_gpio_init(&gp1);
+   _Bool value = 1;
+   // setup second gpio
+   gp2.port = 4;
+   gp2.config = OUTPUT_OPEN_DRAIN_NO_PULL;
+   hosal_gpio_init(&gp2);
+
+   xTaskCreate(my_task, "second_entry", 1024, NULL, 15, NULL);
+
+   while (1) {	// should never reach this point
+      hosal_gpio_output_set(&gp1, value );
+      value = !value;
+      vTaskDelay(pdMS_TO_TICKS(500));
+   }
+}
+

Tasks are created using the function in line 30. You need to specify the entry callback function here (as first parameter) and have it defined. The external task we define here is my_task in line 7. Other parameters are as follows:

  • "second_entry": A descriptive name for the task. This is not used by FreeRTOS in any way. It is included purely as a debugging aid.

  • 1024: The stack depth. Note the value is number-of-bytes / 4. Learning to choose the right size is an artform you pickup as you work through the examples.

  • NULL: Parameter to our callback function. Here we're passing nothing.

  • 15: The task Priority. See below.

  • Null: Not used.

In this example we maintain our blinky program in main that toggles every 500ms. In addition to this we are creating a task for a secondary blinky on port D4, with a different frequency... This example shows how an off-the self kernel eases our multitasking.

Priorities#

The SDK sets the maximum number of priorities to 32. See configMAX_PRIORITIES in bl_iot_sdk/components/platform/soc/bl602/freertos_riscv/config/FreeRTOSConfig.h.

Higher the value: higher the priority. If two tasks have the same priority a round robin scheduling is done. To modify the priority of a running task use vTaskPrioritySet(n).

Queues#

Semaphores#

Mutex#

\ No newline at end of file diff --git a/blog/green-energy/index.html b/blog/green-energy/index.html new file mode 100644 index 0000000..745f88e --- /dev/null +++ b/blog/green-energy/index.html @@ -0,0 +1 @@ +Go nuclear!

Go nuclear!

2023-05-21

Although this discussion is not related to embedded systems at all, I think it's quite relevant to the current times with the energy prices sky-rocketting.

When it comes to switching to Green Energy, I believe going nuclear is the answer. Here's why.

Solar and wind are trickle sources. That is they are sensitive to seasonal variations. They can be great as a complementary secondary source. Otherwise as primary source they'll require large battery storages, which will be unfavourable towards the overall carbon footprint and the prohibitting expense.

We've had feasible nuclear fission technology for nearly a century, and promises of electricity too cheap to meter almost 80 years ago... Only 10% of the world's total electric generation is powered by nuclear today (With France exclusively being the pioneers at over 70%). Why is the adoption so slow?

electric production breakdown

It'll probably have to do with the precedence set by nuclear technology in the global stage. Think atomic bombings of World War II. It had set such a bitter mood among the public that even decades later is hard to rid of. There is also the accidents of Chernobyl and Fukushima to blame. And the latter is rather recent (2011).

The technology has come a long way... And with Molten Salt Reactors, Thorium fuel is used for abundant power and are categorised as intrinsically safe1.

With soaring world temperatures and energy costs there's never been more urgency in switching to green energy. And I think it is when you commit and deploy the technology, you get into a feedback cycle where you incrementally improve. That's when the money pours in for research and the technology matures. And when countries gain energy independance it could take a chunk off the current world's problems...

With all the push with solar and wind energy lately, I wish there was more enthusiasm about Nuclear power. What do you think about Nuclear power generation?

\ No newline at end of file diff --git a/blog/index.html b/blog/index.html new file mode 100644 index 0000000..62bf416 --- /dev/null +++ b/blog/index.html @@ -0,0 +1 @@ +My Blog
update
Zephyr-7b-beta: Or how to run a ChatGPT alternative on an 8GB Graphics Card 2023-11-14 Checking out a free opensource ChatGPT alternative: Zephry-7b-Beta 2023-11-12 Introduction to AliExpress for the DIY Enthusiast 2023-11-10 Mastering the Llama: Unleashing the Power of Self-Hosted AI with Llama GPT 2023-11-10 Switched site to Zola SSG 2023-11-03 Go nuclear! 2023-05-21 My experience with drop-ship warehousing 2023-05-17 My experience with Debian Testing 2023-05-11 Selfhosted email delivery with postfix 2022-12-27 Sphinx Documentation Generator 2022-12-07 Sipeed BL702 board 2022-12-01 Piracy on a Budget 2022-11-24 Gitlab Selfhohsted! 2022-11-23 DOOM Emacs intro 2022-11-16 Self hosted Mastodon Instance 2022-11-13 Review: PI Pico 4MB version 2022-11-04 Octave Command Line Mode 2022-02-27 THE Dev board you've been waiting for: $1.8 XT-ZB1 Zigbee & BLE devkit features BL702 RISC-V module 2022-02-20 Welcome 2022-02-16
riotos
First project in RIOT-OS: OLED 2023-11-10
misc
Building an affordable electronics lab for hobbyists 2023-11-09 Lutris: The Linux Gaming Hero 2023-11-09 Unveiling the Swiss Army Knife of Embedded Hobbyists: The Multimeter 2023-11-09 Building a Frugal Gaming PC 2023-11-08 Best microcontroller for Beginners 2023-11-05 Comparison of SSG frameworks 2023-11-05 Review of Zola 2023-11-05 Deploying an IPv6 based seedbox 2023-09-16 Building a Developer's Workstation 2023-05-23 Basic commands for Network Troubleshooting 2023-05-16 DIY NAS 2023-05-10 802.15.4 Wireless 2023-04-22 Switching to the nRFMicro 2022-03-20 Thoughts on the ESP-IDF SDK 2022-03-19 Switching to the ESP32-C3 2022-03-18 Low-cost microcontrollers for hobbyists 2022-03-16 Analog Filters Primer 2022-02-27 A DIY jtag debugger 2022-02-16
nrf
nRF52840 board (Nice!Nano clones) 2023-11-07 Project: Gravity Detector 2023-05-06 Micro:Bit IO breakout board 2023-05-05
asm
C linker scripts howto 2022-11-16
bl702
Debugging BL702 2022-03-17 SSD1306 OLED Display 2022-03-17 Board Setup Config 2022-03-15 Device Drivers 101 2022-02-24 freeRTOS Overview 2022-02-24 PWM 2022-02-24 Serial UART 2022-02-24 Timers 2022-02-24 Basic GPIO BL702 2022-02-23 Basic application structure 2022-02-22 Setup a development environment 2022-02-22
nuttx
NuttX Supported Boards 2022-02-18
\ No newline at end of file diff --git a/blog/ipv6-box/index.html b/blog/ipv6-box/index.html new file mode 100644 index 0000000..7ef508f --- /dev/null +++ b/blog/ipv6-box/index.html @@ -0,0 +1,42 @@ +Deploying an IPv6 based seedbox

Deploying an IPv6 based seedbox

2023-09-16

With VPS that run purely IPv6 stack being cheaper (for example HostBRR offerings); I'm doing a write-up on how to setup a seedbox running IPv6.

Please keep in mind, you need to have IPv6 connectivity on your home computer/internet to access your services on the VPS. To check if you do head over to https://ip6.biz/.

Install docker and docker compose on your machine first. Once this is done we can then setup our services.

Due to some of the docker image registry sources operating in ipv4 environment, they will be unreachable from our ip6 only box! We have to manually provision a method to make the interconnection work. This is only temporary though, only till we pull our images. After that we will revert to ip6 only operation.

Change Nameservers#

To achieve this translation, we will need to adjust our nameservers as follows (courtesy https://nat64.net/).

nameserver 2a01:4f9:c010:3f02::1
+nameserver 2a00:1098:2c::1
+nameserver 2a00:1098:2b::1
+

Although this is not as straight forward as it seems. The file /etc/resolv.conf cannot be edited by hand as it will be update to any network changes. Attempting to use the package resolvconf deemed fruitless for me. So I resorted to the trick method of editing this file:

sudo apt install e2fsprogs -y # install needed package
+cd ~
+cp /etc/resolv.conf . # Make a backup of the original file
+sudo rm -f /etc/resolv.conf
+sudo vim /etc/resolv.conf # insert the above file contents as nameservers
+sudo chattr +i /etc/resolv.conf
+sudo systemctl restart networking
+

Now setup your services as needed. Docker can access and pull from any generic registries.

Once the docker pulls are done:

sudo chattr -i /etc/resolv.conf
+sudo cp ~/resolv.conf /etc/resolv.conf
+sudo systemctl restart networking
+

Now our box is back to IPv6 only state.

One downside with this system is that you can't implement auto update of docker images using watchtower. Since once again some of the docker registeries are inaccessible.

Enable IPv6 in Docker#

One last concern is that you need to enable IPv6 networking in docker so that the containers can reach the outside world. This is explained in Enable IPv6 support. Basically you have to edit the file /etc/docker/daemon.json.

Once that's done you need to create a new IPv6 network and use that with your containers. Here's a sample compose file to run the qBittorrent image:

version: "3"
+services:
+  qbittorrent:
+    image: ghcr.io/hotio/qbittorrent
+    container_name: qbittorrent
+    networks:
+      - ip6net		# <---- add the container to the network
+    environment:
+      - PUID=1000
+      - PGID=1000
+      - TZ=Etc/UTC
+      - WEBUI_PORT=8080
+    volumes:
+      - /home/ajit/.config/appdata/qbtorrent:/config
+      - /home/ajit/downloads:/downloads
+    ports:
+      - 8080:8080
+      - 6881:6881
+      - 6881:6881/udp
+    restart: unless-stopped
+
+networks:
+  ip6net:
+    enable_ipv6: true
+    ipam:
+      config:
+        - subnet: 2001:0DB8::/112
+
+
\ No newline at end of file diff --git a/blog/linux-gaming/index.html b/blog/linux-gaming/index.html new file mode 100644 index 0000000..6972222 --- /dev/null +++ b/blog/linux-gaming/index.html @@ -0,0 +1 @@ +Lutris: The Linux Gaming Hero

Lutris: The Linux Gaming Hero

2023-11-09

Hey there, fellow tech enthusiasts! Today, I'm taking a little detour from our usual DIY projects to dive into the exciting world of Linux gaming. Yeah, you heard me right – gaming on Linux is a thing, and it's not just for tech wizards in their secret lairs. Diving into the realm of Linux gaming, we often find ourselves tangled in a web of emulators, Wine dependencies, and a barrage of terminal commands that can intimidate even the most ardent of enthusiasts. Enter Lutris, the unsung hero of Linux gaming that has been quietly revolutionizing our gaming experiences over the past few years.

Tux gaming

Now, if you're like me, you might have witnessed the evolution of tooling and development environments in the embedded world. Well, the same winds of change have been blowing in the gaming sphere, especially for our Linux buddies. In the last decade, the progress has been nothing short of remarkable.

Gone are the days when Linux was just a powerhouse for servers and a playground for developers. It's the dawning of a new era, where Linux doesn't just mean business; it's serious about play, too. Enter Lutris, a game changer (pun intended!) that's been turbocharging the Linux gaming scene in recent years.

Lutris, in its essence, is an optimizer's dream. It transforms the often labyrinthine task of game management into a streamlined, unified process. With Lutris, you're not just running games; you're curating an entire library with a tool that's as versatile as a Swiss Army knife.

The beauty of Lutris lies in its simplicity. Installation scripts, courtesy of the community's collective wisdom, are your roadmap to hassle-free gaming. These scripts are like the secret sauce that makes a good dish great—they handle the complexities of configuration so you can focus on what matters: the game itself.

So, what's the deal with Lutris? Well, it's a game manager, sure, but it's also a treasure trove for tweaking and optimizing your gaming experience. It supports a plethora of games, including those Windows titles that never thought they'd see the light of a Linux desktop. With Lutris, you can manage game installations, customize settings, and even sync your game library across different platforms.

The underlying engine - Proton stands as a crucial element in the realm of Linux gaming compatibility, developed by Valve Corporation. It functions as a compatibility layer, enabling the seamless execution of Windows games on the Linux operating system. Leveraging the power of Vulkan and other technologies, Proton strives to overcome the historical barrier between Windows-exclusive titles and the Linux platform. Under constant development, it incorporates optimizations and improvements to enhance compatibility with a wide array of games. With its integration into the Steam client, Proton has significantly expanded the gaming library accessible to Linux users, contributing to the ongoing efforts to establish Linux as a viable gaming platform. Its serious approach to delivering compatibility aligns with the industry's shift towards broader inclusivity and accessibility in gaming.

From a technical standpoint, Lutris leverages compatibility layers like Wine and Proton to bridge the gap between Linux and Windows game libraries. It's like having a translator that enables games written for one platform to speak the language of another. This technical wizardry is crucial for expanding the gaming catalog on Linux.

Now, let's address the elephant in the room – running pirated games on Linux. In my experience Lutris does a better work of running these than the native Windows OS! Lutris has a handy Install from EXE option that's handy just for this case. You just have to mount the download (if required) and point Lutris to the Setup executable. Easy! I use a separately partitioned instance of PoPOS and have lutris installed on it for my gaming needs. This is a way to declutter from my main PoPOS install for my dev environment needs. As installing games tend to be time intensive, I can leave the gaming partition alone when I upgrade/reinstall the workstation one.

There you go! An overview of Lutris and its significant contribution to elevating the gaming experience on Linux. It’s a testament to what the open-source community can achieve, and a beacon for gamers looking to Linux as a viable gaming platform.

\ No newline at end of file diff --git a/blog/llama-howto/index.html b/blog/llama-howto/index.html new file mode 100644 index 0000000..400e05d --- /dev/null +++ b/blog/llama-howto/index.html @@ -0,0 +1,15 @@ +Zephyr-7b-beta: Or how to run a ChatGPT alternative on an 8GB Graphics Card

Zephyr-7b-beta: Or how to run a ChatGPT alternative on an 8GB Graphics Card

2023-11-14

Recently we have seen the rise of the small LLMs. As discussed in this article the Zephyr-7b-beta model is making ground for it's impressive results. Here we shall see how to run the model locally on your system, all you need is a GPU with 8GB VRAM. My workstation sports an AMD RX 580 card that is ideally suited for this task! While this guide is targetted towards AMD cards note that you can run it on NVIDIA as well. On my system it runs at speed around 7-9 tokens/sec.

We will use the 'Text generation web UI' https://github.com/oobabooga/text-generation-webui as our client. This is the software that helps us to host and use the LLM model.

First we need to install what is called ROCm. This is a library that works on top of the AMD GPU driver. The following steps assume you're running POP-OS 22.04. You can follow this guide. We need to install version 5.6 (as of Nov 2023). The requirement is dictated by 'Text generation web UI', so see its documentation for the exact version you need in future.

You may wish to install radeontop, an app that'll give various deets of the running status of the GPU. And run it alongside in another terminal while you prompt away.

Once ROCm is installed we can proceed with installing 'Text generation web UI'.

First we need to set an environment variable:

export ROCBLAS_TENSILE_LIBPATH=/opt/rocm/lib/rocblas/library/
+

You'll have to run the above evertime before you invoke the 'Text generation web UI' with ./start_linux.sh or the app will crash when you load in your model.

Finally:

git clone https://github.com/oobabooga/text-generation-webui.git
+cd text-generation-webui
+./start_linux.sh
+

That last command is a convenient script that on first run does the actual installation. It will create the directory installer_files. When prompted for the type of installation enter B for AMD GPU.

It'll download a couple of GBs and take some time.

Once finished open up the displayed link in your browser. You'll be greeted with the 'Text generation web UI' interface.

The settings will be easily overwhelming at first with lots of variables but I'm going to present here what worked on my 8GB AMD card!

Obviously first we need to download a model. Go to the Model tab and enter TheBloke/zephyr-7B-beta-GGUF in the Download Model field. In the second File name box enter: zephyr-7b-beta.Q4_K_M.gguf which is recommended for 8GB cards. You can see details in the HuggingFace card page.

Once it's downloaded push the reload button next to the model name blank on the top and then select the model name. Then set the following options below:

n-gpu-layers: anything above 35
+n_ctx: 8000
+

Finally press load. This will take some time. Then goto Parameters tab. Under Generation:

max_new_tokens: 2000
+top_p: 0.95
+top_k: 40
+

Under Instruction Template: Choose ChatML.

That's it for settings.

Now go to Default tab and paste the following prompt:

<|system|>
+You are a creative writing assistant
+<|user|>
+<your prompt here>
+<|assistant|>
+

Substitue it with your prompt and have fun!

Zephyr-7b brings the excitement of a capable model into the hands of affordable 8Gig GPU cards. Very quickly you'll be amazed at the results, although there is some telling difference from the present benchmark - GPT-4!

\ No newline at end of file diff --git a/blog/llama/index.html b/blog/llama/index.html new file mode 100644 index 0000000..52b9710 --- /dev/null +++ b/blog/llama/index.html @@ -0,0 +1 @@ +Mastering the Llama: Unleashing the Power of Self-Hosted AI with Llama GPT

Mastering the Llama: Unleashing the Power of Self-Hosted AI with Llama GPT

2023-11-10

In the vibrant realm of artificial intelligence and machine learning, the democratization of technology has been a game-changer. The latest buzz in the tech community is about Llama GPT, a free, open-source alternative to ChatGPT that enthusiasts can self-host. For those of us who relish the control and customization that comes with running software on our own machines, Llama GPT is like a beacon in the AI space.

I recently decided to bring Llama GPT into my personal fold. There’s something incredibly satisfying about hosting such powerful technology locally. Llama GPT offers a range of model sizes to cater to different needs and capacities, and I opted for the 13B version, with a model size of roughly 7GB.

The prerequisite for running Llama GPT on Linux is Docker, which aligns perfectly with the setup on my Intel Xeon E5 2670v3-powered workstation. As I’ve shared in a previous blog post, my system sports 12 cores and 24 threads, a configuration that one would presume quite capable of handling a large language model.

Llama logo on PC

However, my initial enthusiasm met with a challenge. The response times were sluggish, averaging around a minute per response. This was a stark contrast to the snappy interactions I've had with the online ChatGPT.

Quality-wise, the responses from the 13B model of Llama GPT didn't quite measure up to the standards set by GPT-4, which wasn't entirely surprising. The 13B model is a mid-tier offering, and I hadn't sprung for the massive 70B model, which requires a daunting 41GB of system RAM.

In search of a benchmark, I stumbled upon a free online version of the 70B model. However, this service appeared to have a cap on the number of words per interaction, which made a head-to-head comparison with GPT-4 challenging.

This experience got me thinking about the trade-offs between self-hosted and cloud-based AI models. Self-hosting offers several advantages, like data privacy, no API costs, and the freedom to tweak the system to your liking. But it also comes with its own set of challenges, such as the need for significant computational resources and the potential for slower response times, as I learned firsthand.

To give you a better idea, running a 13B model like Llama GPT is no small feat. It requires not only a powerful processor but also a substantial amount of RAM. While my Xeon processor is no slouch, AI model inference speed depends heavily on parallel processing capabilities, something that's inherently optimized in cloud-based solutions like ChatGPT, which run on specialized hardware designed for high-performance computing tasks.

Furthermore, the sheer size of these models and the resources they require to run smoothly means that self-hosting is often not as cost-effective or energy-efficient as using a cloud service. Cloud providers can leverage economies of scale, deploying thousands of optimized processors to serve millions of users, which individual self-hosters simply can't match.

Despite these hurdles, the allure of a self-hosted AI model remains. For hobbyists and tinkerers, it's an exciting prospect to have your own AI that you can interrogate and interact with on your terms, without sending data across the internet. It's a sandbox for experimentation and learning that aligns with the ethos of DIY and hands-on discovery.

But it's essential to manage expectations. As of now, self-hosted AI models like Llama GPT serve more as educational tools and proofs of concept rather than competitors to established cloud-based services. They offer a glimpse into the inner workings of language models and allow enthusiasts to experiment with AI on their terms.

To wrap up, while my experiments with Llama GPT may not have delivered the speed and quality of ChatGPT, they've provided invaluable insights into the capabilities and limitations of self-hosting large-scale AI models. For those in the embedded development and IoT community, it's a reminder of the exciting possibilities and the practical considerations that come with the territory of cutting-edge tech. It’s a balancing act between the thrill of self-hosting and the performance of cloud-based services—a decision each of us in the community will weigh based on our individual needs and curiosity.

\ No newline at end of file diff --git a/blog/llama2/index.html b/blog/llama2/index.html new file mode 100644 index 0000000..0bb23a0 --- /dev/null +++ b/blog/llama2/index.html @@ -0,0 +1,6 @@ +Checking out a free opensource ChatGPT alternative: Zephry-7b-Beta

Checking out a free opensource ChatGPT alternative: Zephry-7b-Beta

2023-11-12

In a previous article we reviewed the free LLM Llama2, the 70b version. This is prohibitively large to run on your local machines as it requires around 48GB of GPU VRAM. I instead tried the 13b version but it was still too large for my GPU with only 8Gigs of VRAM. So ran it on my CPU instead and it was painfully slow.. Here we will look at a viable alternative: Zephry-7b-Beta, the latest sensation in the world of natural language processing!

Zephry-7b-Beta is a groundbreaking large language model that has been capturing the attention of the scientific community due to its exceptional abilities in natural language processing, despite being a relatively small model. This innovative language model, developed by the research wing of HuggingFace, boasts impressive feats that challenge the traditional notion that larger models are better. Although Zephry-7b-Beta has just over 7 billion parameters, in many benchmarks, outperforms larger models like GPT-3 and BERT-Large, which have more than 175 billion parameters each. This remarkable performance has been attributed to the model's unique architecture and training methods, which have allowed it to achieve a balance between accuracy and efficiency, making it an ideal candidate for real-world applications where resource constraints can pose a challenge. As in the case of hosting on a home PC.

It gives out an impressive interactive experience. And I found that it resembled GPT4 more than the Llama 2 model.

I first sought to install it locally as this is reported to run comfortably on 8G GPUs. So I downloaded the prerequisite library ROCm (for driving the AMD GPU), and then installed Text generation web UI. Downloaded the LLM model from HuggingFace site. Some of the troubleshooting steps I took can be seen in this thread. But again it was too slow to be usable.. Maybe I'm doing something wrong.

Then I resorted to hosting on a cloud GPU provider. I made an account at vast.ai (these folks being the cheapest). I made an RTX A6000 (48GB VRAM) instance with 8G disk space. Downloaded the same model image (the Q5_K_M variant). Then I made parameter choices in Text generation web UI (not necessarily optimum, but sure something to play around with and test):

  • Set Generation/Preset to debug-deterministic
  • Increase max new tokens to 2000
  • ChatML for Instruction template

Also remember to set n-gpu-layers to over 35 for this gguf model when loading it.

There are plenty of Youtube videos on how to use Text generation web UI.

And used the following prompt (copied from the model page and tweaked):

<|system|>
+You are a creative writing assistant
+<|user|>
+<your prompt here>
+<|assistant|>
+

I have to say.. The results were snappy. And like I said earlier the responses were impressive.

Although I have been using the Default/Notebook input method of the Text generation web UI, I'm looking to study the chat prompt. And see if it gives a more fluid interactive experience.

This has been my log of trying out a free new ChatGPT alternative. Although I didn't know why it didn't run well on my local system since this is most suited for this task. Let me know what your goto model is these days!

\ No newline at end of file diff --git a/blog/mastodon/index.html b/blog/mastodon/index.html new file mode 100644 index 0000000..5b0e13b --- /dev/null +++ b/blog/mastodon/index.html @@ -0,0 +1,20 @@ +Self hosted Mastodon Instance

Self hosted Mastodon Instance

2022-11-13

In keeping with the times, I messed about with setting up an own Mastodon self-hosted instance!

An ideal VM is the Oracle's OCI cloud service. In there they provide a 4vcore, 24GB ram(!) as a free service. Which is plenty for this purpose. In contrast to the other competitors offering which I found is actually insufficient for self hosting Mastodon. Additionally you get 150G block storage free (in addition to 50G boot volume). Oracle calls it "VM.Standard.A1.Flex".

I instead went ahead with LUXVPS. They allow open port on 25 (upon request) for sending emails from local machine without a middleman subscription (and they get expensive quick). They're prices are also very affordable; for 3EUR you get a 1c / 4GB VM with KVM virtualisation. In my experiment this is sufficient... My instance with two test users, with federation relay and full text search only consumes 1G Ram and plenty of processor left. Should be enough for a small gathering :).


If you're taking the local server option for emails, you have a good guide here: https://doc.dovecot.org/configuration_manual/howto/postfix_and_dovecot_sasl/. Setting up your own local mail server is tricky with a combination of packages required. This is due to the fact most mail providers (especially Google's GMail) require SASL authentication. But it's worth the effort I think. Again one thing you cannot avoid is that your setup will send the mail almost invariably to the users SPAM folder! But you can always opt for a mail forwarder subscription.

Following on from the above link, some lines has to be commented in /etc/postfix/master.cf:

submission inet n - n - - smtpd
+  -o smtpd_tls_security_level=encrypt
+  -o smtpd_sasl_auth_enable=yes
+  -o smtpd_sasl_type=dovecot
+  -o smtpd_sasl_path=private/auth
+  -o smtpd_sasl_security_options=noanonymous
+  -o smtpd_sasl_local_domain=$myhostname
+#  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
+#  -o smtpd_sender_login_maps=hash:/etc/postfix/virtual
+  -o smtpd_sender_restrictions=reject_sender_login_mismatch
+#  -o smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject
+

Here's the mastodon config (/home/mastodon/live/.env.production)relating to smtp:

SMTP_SERVER=localhost
+SMTP_PORT=587
+SMTP_LOGIN=
+SMTP_PASSWORD=
+SMTP_FROM_ADDRESS=notifications@yourdomain.online
+SMTP_AUTH_METHOD=plain
+SMTP_OPENSSL_VERIFY_MODE=none
+SMTP_ENABLE_STARTTLS_AUTO=true
+

Once you get a VM instance up and running, continue with the official help page: https://docs.joinmastodon.org/admin/install/. I installed on a Debian 11 image.

An 'errata' to the last bit with certbot has input here if you run into problems: https://github.com/mastodon/documentation/issues/940. Don't forget the prerequisites: https://docs.joinmastodon.org/admin/prerequisites/

My instance setup is accessible via https://simplysocial.online/1. Users can create accounts on this instance!

Stay in touch for further discussion on integrating cloudflare with Backblaze for free egress, in bringing costs down further!

1

In Ubuntu 22.04 you need to run sudo usermod -a -G mastodon www-data to fix permissions issue: https://github.com/mastodon/mastodon/discussions/19651

\ No newline at end of file diff --git a/blog/mcu-selection-guide/index.html b/blog/mcu-selection-guide/index.html new file mode 100644 index 0000000..21c09d8 --- /dev/null +++ b/blog/mcu-selection-guide/index.html @@ -0,0 +1 @@ +Low-cost microcontrollers for hobbyists

Low-cost microcontrollers for hobbyists

2022-03-16

There are a multitude of chip manufacturers, let alone individual chip modules. It can get overwhelming quickly for a newbie in the field of embedded or IOT systems, especially if all you want to do is tinker at a hobbyist level.

I present to you a selection guide based on 3 modules to serve a wide range of applications. The rationale being, by having only a few select chips in your toolset you can put the effort to master your chips and get into the nitty gritty. This leads to less frustration when developing and also by working on the native toolsets for the chip in hand, you're confident in producing the most efficient code.

With this in mind I present to you the following list1:

  • Bluepill (stm32f103c8)2: sub-dollar chip that's simple and very compact formfactor. Deploy it for the simplest of applications. You can still run freeRTOS! And even drive an OLED display! An excellent intro using free licence tools are covered in the book: Beginning STM32 by Warren Gay.

  • Micro:Bit (nRF52833): low power wireless! When it comes to low power wireless, Nordic is the undisputed king. RF is in their name and they have gained good popularity in the industry over the past years. See this post for viable hobbyist boards.

  • XT-ZB1 (bl702): the king of versatility! Although intended for IOT applications, the $2 price tag opens the possibilities to deploy into anything! A RISC-V clocked at 144 MHz including FPU. That’s decent enough for most of demanding applications. Note that the fact the SDK is still in developement (after 2 years of release!) and that it's unsupported by any IOT RTOS frameworks (RT-Thread for example) makes is a questionable choice right now and we go with the above chip for wireless.

Between the Bluepill and the nRF52, I believe you have the world of embedded at your hands!

1

If you're interested in beginner boards or controller see this post.

2

Edit: See this post about the PI Pico variant. This would be an excellent replacement for the bluepill. With 4M flash and 130MHz M0+ processor it is a no brainer for new projects!!

\ No newline at end of file diff --git a/blog/micro-bit-breakout/index.html b/blog/micro-bit-breakout/index.html new file mode 100644 index 0000000..81755a5 --- /dev/null +++ b/blog/micro-bit-breakout/index.html @@ -0,0 +1,26 @@ +Micro:Bit IO breakout board

Micro:Bit IO breakout board

2023-05-05

As we have seen prevously, we have switched our interest to the Nordic family of chips.

Providing a whole portfolio of low-power wireless microcontrollers, we are spoiled for choice when it comes to IoT centric projects. Given that 99% of embedded projects these days is somehow IoT based, this is the rationale behind going for these chips. Infact RF is in the name for Nordic, think nRF (the name of their mcu range).

In a wide range of Nordic chips our go-to is the nRF52833 controller. I find it has the right balance of cost vs performance. Detailed info of this chip here.

Now there are two viable hobbiest boards for this MCU1.

  • nRF52840 Dongle [Which uses a similar chip only difference being double RAM/ROM]
  • Micro:Bit v2

bbc micro:bit

I choose the Micro:Bit. Here's why.

It packs in a lot of on-board sensors, which can be beneficial to quickly getting up and running; and at a reasonable price overhead. It retails for around $17.

It is also widely available.

Although its initial concept was to aid in kids' programming education; it has found its way into the arsenal of the serious firmware developer. Now isn't that a very familiar story?

But there is one hurdle in using the Micro:Bit in breadboarding prototypes. There is a solution to this. Enter, the T-type breakout board.

breakout board pic

It can be procured from Aliexpress here. Here's an excerpt of the board's device tree in Zephyr that conveniently shows the GPIO mapping of the nRF52833 to the marked pins in the breakout board.

edge_connector: connector {
+		compatible = "microbit,edge-connector";
+		#gpio-cells = <2>;
+		gpio-map-mask = <0xffffffff 0xffffffc0>;
+		gpio-map-pass-thru = <0 0x3f>;
+		gpio-map = <0 0 &gpio0 2 0>,	/* P0 */
+			   <1 0 &gpio0 3 0>,	/* P1 */
+			   <2 0 &gpio0 4 0>,	/* P2 */
+			   <3 0 &gpio0 31 0>,	/* P3 */
+			   <4 0 &gpio0 28 0>,	/* P4 */
+			   <5 0 &gpio0 14 0>,	/* P5 */
+			   <6 0 &gpio1 5 0>,	/* P6 */
+			   <7 0 &gpio0 11 0>,	/* P7 */
+			   <8 0 &gpio0 10 0>,	/* P8 */
+			   <9 0 &gpio0 9 0>,	/* P9 */
+			   <10 0 &gpio0 30 0>,	/* P10 */
+			   <11 0 &gpio0 23 0>,	/* P11 */
+			   <12 0 &gpio0 12 0>,	/* P12 */
+			   <13 0 &gpio0 17 0>,	/* P13 */
+			   <14 0 &gpio0 1 0>,	/* P14 */
+			   <15 0 &gpio0 13 0>,	/* P15 */
+			   <16 0 &gpio1 2 0>,	/* P16 */
+			   <19 0 &gpio0 26 0>,	/* P19 */
+			   <20 0 &gpio1 0 0>;	/* P20 */
+};
+
1

For an updated post of a newly available alternative see this post.

\ No newline at end of file diff --git a/blog/multimeters/index.html b/blog/multimeters/index.html new file mode 100644 index 0000000..195d796 --- /dev/null +++ b/blog/multimeters/index.html @@ -0,0 +1 @@ +Unveiling the Swiss Army Knife of Embedded Hobbyists: The Multimeter

Unveiling the Swiss Army Knife of Embedded Hobbyists: The Multimeter

2023-11-09

Embarking on the thrilling journey of embedded development is akin to diving into a sea of components, wires, and circuits. Amidst this electronic labyrinth, there exists a humble yet indispensable tool—the multimeter. Today, let's unravel the mysteries of this electronic wizardry and explore why every embedded hobbyist should have a trusty multimeter by their side.

Using a multimeter

  1. Versatility Personified:

    • Voltage Measurements: From checking power supplies to ensuring proper voltage levels, a multimeter is your go-to tool for accurate voltage measurements.
    • Current Readings: Understand the flow of current through your circuit with ease, helping you troubleshoot and optimize power consumption.
  2. Resistance to the Rescue:

    • Continuity Testing: Track down open circuits and ensure seamless connectivity by employing the multimeter's continuity testing feature.
    • Resistance Measurements: Measure resistances in your components, aiding in identifying faulty elements and optimizing circuit design.
  3. Diagnosing Component Health:

    • Testing Capacitors: Verify the health of capacitors by measuring capacitance, helping you identify and replace aging or faulty components.
    • Inductance Measurements: For those delving into the world of inductors, the multimeter assists in gauging inductance levels and diagnosing issues.
  4. Precision in Troubleshooting:

    • Fault Detection: Pinpoint faults and discrepancies in your circuitry, saving valuable time in the debugging process.
    • Temperature Measurements: Some advanced multimeters even offer temperature measurements, adding another layer of diagnostic capability.
  5. Safety First:

    • Non-Invasive Testing: Conduct non-invasive tests without interrupting your circuit, ensuring the safety of both your components and yourself.
    • Fuse Checks: Before powering up a circuit, use the multimeter to check fuses for continuity, preventing potential damage to your setup.
  6. Essential for Prototyping:

    • Prototyping Validation: During the prototyping phase, a multimeter aids in validating your circuit design and ensuring it aligns with your expectations.
    • Real-Time Monitoring: Keep a close eye on voltage levels and other parameters in real-time, allowing for on-the-fly adjustments and improvements.
  7. Cost-Effective Investment:

    • Budget-Friendly: Multimeters come in a range of prices, making them an affordable yet invaluable investment for any embedded hobbyist.
    • Longevity: With proper care, a good-quality multimeter can last for years, offering a long-term solution for your electronic endeavors.
  1. Neoteck Pocket Digital Multimeter:

    • Compact and Affordable: The Neoteck Pocket Multimeter packs essential features into a budget-friendly package, making it ideal for beginners and hobbyists looking for a reliable yet economical option.
  2. Aneng DM850 Digital Multimeter:

    • Budget-Friendly Excellence: With accurate readings, essential functionalities, and a user-friendly design, it stands out as an excellent choice for hobbyists and beginners seeking reliability without breaking the bank.
  3. Etekcity Digital Multimeter MSR-R500:

    • Budget-Friendly Precision: With a focus on simplicity and accuracy, the Etekcity MSR-R500 is a cost-effective choice for those seeking basic functionalities without breaking the bank.
  4. AstroAI Digital Multimeter TRMS 6000 Counts:

    • Versatility on a Budget: The AstroAI TRMS Multimeter provides an impressive range of features at an economical price point, catering to hobbyists who need reliability without splurging.
  5. Tekpower TP8268 AC/DC Auto/Manual Range Digital Multimeter:

    • Affordable Auto-Range Functionality: The Tekpower TP8268 offers auto-range capabilities without sacrificing affordability, making it a practical choice for users who want convenience on a budget.

In the world of embedded hobbyism, where creativity meets thriftiness, the multimeter proves to be an invaluable asset without burning a hole in your wallet. As we've delved into the capabilities of these cost-effective examples, it's evident that you don't need to break the bank to access the essential functionalities for your electronic projects.

So, whether you're a seasoned pro or just starting your embedded journey, consider the multimeter your electronic sidekick. With its ability to unravel the intricacies of your circuits and provide real-time insights, it's not just a tool; it's the key to unlocking the full potential of your projects. Embrace the power of the multimeter, and let your embedded endeavors flourish. Happy tinkering!

\ No newline at end of file diff --git a/blog/net-commands/index.html b/blog/net-commands/index.html new file mode 100644 index 0000000..16cb5d3 --- /dev/null +++ b/blog/net-commands/index.html @@ -0,0 +1,16 @@ +Basic commands for Network Troubleshooting

Basic commands for Network Troubleshooting

2023-05-16

In this post we will look at a couple commands most used for network troubleshooting.

ping#

ping is the most simplest network command that everyone is familiar with. You can use it to check if a node (or device) is connected and running. Note however, that if this feature has been disabled on the node it will not respond to pings!

The command takes the following format:

ping <target_ip>
+

traceroute#

traceroute is a more fancy command. It gives us the ping results of each hop (eg routers) along the packet's path. So it can be used to deduce where in the network the packet is failing. The command takes the following format:

traceroute <target_ip>
+

For example it can be used to troubleshoot problems with our selfhosted email [scenario]({% post_url /update/2022-12-27-postfix_mail %}). By running the command aimed at the Google's mail servers (for example) from our box, we can deduce if the email is failing whithin the VPS provider's network. Which would mean the provider has blocked port 25. In this case we would get the following behaviour.

# traceroute -n -T -p 25 gmail-smtp-in.l.google.com
+traceroute to gmail-smtp-in.l.google.com (173.194.204.27), 30 hops max, 60 byte packets
+1 192.210.145.2 0.033 ms 0.012 ms 0.011 ms
+2 * * *
+3 * * *
+4 * * *
+5 * * *
+.
+.
+.
+30 * * *
+

Since there is no reponse from the very next node it is likely failing within the provider's network and we can say that the provider is actively blocking it.

nc#

This one is called netcat. And it can be used to pass messages between the computers through network sockets. If you work on building an IoT device you will find this command incredibly useful.

You can run it in server and client mode. To run it in server mode:

nc -l <port>
+

To runt it in client mode:

echo "hello there" | nc <remote-ip> <remote-port>
+

You get the idea. If you omit the pipe command and run only the nc command, you'll get an interactvie prompt. Much like a telnet session!

As you can see this is a great way to set up makeshift listening servers on a specific port for IoT devices ad clients to connect to and debug the communication.

\ No newline at end of file diff --git a/blog/nrf-clones/index.html b/blog/nrf-clones/index.html new file mode 100644 index 0000000..e2ed604 --- /dev/null +++ b/blog/nrf-clones/index.html @@ -0,0 +1 @@ +nRF52840 board (Nice!Nano clones)

nRF52840 board (Nice!Nano clones)

2023-11-07

These Chinese Nicenano V2 clones have started popping up on AliExpress. This is great news for us low-power-wireless nerds; As this widens our choice for ready made boards..

Nordic hasn't actually been welcoming towards hobbyists and this has resulted in scarcity of ready-made newbie friendly boards for prototyping (as opposed to esp32 or the ST's bluepill). Which has been a real game stopper for home IOT projects. We've discussed two possible boards in this post. Here is a new viable option.

NRF52840 Development Board Supermini Compatible With Nice!Nano V2.0

Main board pics

Board pinouts

Features:

  • Mini-sized (breadboard friendly)

  • Built-in LiPo battery charger

  • Supports charger boost jumper (100mA to 300mA) on the back side

Unfortunately the SWD debug pins are not broken out as headers, but as pads underneath. We'd have to directly solder some wires onto this. I haven't checked them out personally, but they may be shipping with a bootloader for simply flashing purposes.

There is a known issue with higher leak current than the original micro board. This one is reported to have a deep sleep current consumption of 700mA! (reddit)

For more details and the workaround of the issue please follow the link to this site.

As the maker and IoT communities continue to grow, it's exciting to see how this clone will contribute to the world of embedded systems and innovation. Whether you're a seasoned developer or just starting your journey, the Nicenano V2 clone is worth exploring for your next project.

\ No newline at end of file diff --git a/blog/nrfmicro/index.html b/blog/nrfmicro/index.html new file mode 100644 index 0000000..52470c1 --- /dev/null +++ b/blog/nrfmicro/index.html @@ -0,0 +1 @@ +Switching to the nRFMicro

Switching to the nRFMicro

2022-03-20

In response to my rants on the low-power wireless situation with the ESP ecosystem, I've finally (hopefully :-) ) decided instead to base on the nRF52 platform using Zephyr RTOS. These chips come with onboard low-power wireless radio including the 802.15.4; ideal for short distance networking.

My biggest gripe was that the ecosystem didn't support hobbyiest level cheap development modules. This is not the case anymore since I found this breakout board project on github. This board can be ordered on PCBWay for like $30 for an assembled 5 bunch.

The board lacks out-of-the box support from Zephyr, but this could easily be solved by an upstream pull-request. Also featuring the board on the Zephyr supported list could bring in some more exposure; I had to ravage through the internet to come across this project!

\ No newline at end of file diff --git a/blog/nuttx-supported-boards/index.html b/blog/nuttx-supported-boards/index.html new file mode 100644 index 0000000..ee3309f --- /dev/null +++ b/blog/nuttx-supported-boards/index.html @@ -0,0 +1,342 @@ +NuttX Supported Boards

NuttX Supported Boards

2022-02-18

NuttX supports a variety of boards. One of the reasons I've settled on Nuttx is it's front row support for cheap chinese boards. Namely the bluepill, esp32 and our favourite the RISC-V DT-BL10 from Doiting.

The official site page for supported boards is a stub. It infact fooled me at first...

You can simply traverse the boards folder to get an uptodate view of the supported boards. For completeness sake I'll list here the list of supported boards at the time of writing:

$ tree boards -dL 3
+boards
+├── arm
+│   ├── a1x
+│   │   └── pcduino-a10
+│   ├── am335x
+│   │   └── beaglebone-black
+│   ├── c5471
+│   │   └── c5471evm
+│   ├── cxd56xx
+│   │   ├── common
+│   │   ├── drivers
+│   │   └── spresense
+│   ├── dm320
+│   │   └── ntosd-dm320
+│   ├── efm32
+│   │   ├── efm32-g8xx-stk
+│   │   ├── efm32gg-stk3700
+│   │   └── olimex-efm32g880f128-stk
+│   ├── eoss3
+│   │   └── quickfeather
+│   ├── imx6
+│   │   └── sabre-6quad
+│   ├── imxrt
+│   │   ├── imxrt1020-evk
+│   │   ├── imxrt1050-evk
+│   │   ├── imxrt1060-evk
+│   │   ├── imxrt1064-evk
+│   │   └── teensy-4.x
+│   ├── kinetis
+│   │   ├── freedom-k28f
+│   │   ├── freedom-k64f
+│   │   ├── freedom-k66f
+│   │   ├── kwikstik-k40
+│   │   ├── teensy-3.x
+│   │   ├── twr-k60n512
+│   │   └── twr-k64f120m
+│   ├── kl
+│   │   ├── freedom-kl25z
+│   │   ├── freedom-kl26z
+│   │   └── teensy-lc
+│   ├── lc823450
+│   │   └── lc823450-xgevk
+│   ├── lpc17xx_40xx
+│   │   ├── lincoln60
+│   │   ├── lpc4088-devkit
+│   │   ├── lpc4088-quickstart
+│   │   ├── lpcxpresso-lpc1768
+│   │   ├── lx_cpu
+│   │   ├── mbed
+│   │   ├── mcb1700
+│   │   ├── olimex-lpc1766stk
+│   │   ├── open1788
+│   │   ├── pnev5180b
+│   │   ├── u-blox-c027
+│   │   └── zkit-arm-1769
+│   ├── lpc214x
+│   │   ├── mcu123-lpc214x
+│   │   └── zp214xpa
+│   ├── lpc2378
+│   │   └── olimex-lpc2378
+│   ├── lpc31xx
+│   │   ├── ea3131
+│   │   ├── ea3152
+│   │   └── olimex-lpc-h3131
+│   ├── lpc43xx
+│   │   ├── bambino-200e
+│   │   ├── lpc4330-xplorer
+│   │   ├── lpc4337-ws
+│   │   ├── lpc4357-evb
+│   │   └── lpc4370-link2
+│   ├── lpc54xx
+│   │   └── lpcxpresso-lpc54628
+│   ├── max326xx
+│   │   └── max32660-evsys
+│   ├── moxart
+│   │   └── moxa
+│   ├── nrf52
+│   │   ├── nrf52832-dk
+│   │   ├── nrf52832-mdk
+│   │   ├── nrf52832-sparkfun
+│   │   ├── nrf52840-dk
+│   │   ├── nrf52840-dongle
+│   │   └── nrf52-feather
+│   ├── nuc1xx
+│   │   └── nutiny-nuc120
+│   ├── phy62xx
+│   │   └── phy6222
+│   ├── rp2040
+│   │   ├── common
+│   │   ├── pimoroni-tiny2040
+│   │   └── raspberrypi-pico
+│   ├── s32k1xx
+│   │   ├── s32k118evb
+│   │   ├── s32k144evb
+│   │   ├── s32k146evb
+│   │   ├── s32k148evb
+│   │   └── ucans32k146
+│   ├── sam34
+│   │   ├── arduino-due
+│   │   ├── flipnclick-sam3x
+│   │   ├── sam3u-ek
+│   │   ├── sam4cmp-db
+│   │   ├── sam4e-ek
+│   │   ├── sam4l-xplained
+│   │   ├── sam4s-xplained
+│   │   └── sam4s-xplained-pro
+│   ├── sama5
+│   │   ├── giant-board
+│   │   ├── sama5d2-xult
+│   │   ├── sama5d3x-ek
+│   │   ├── sama5d3-xplained
+│   │   └── sama5d4-ek
+│   ├── samd2l2
+│   │   ├── arduino-m0
+│   │   ├── circuit-express
+│   │   ├── samd20-xplained
+│   │   ├── samd21-xplained
+│   │   └── saml21-xplained
+│   ├── samd5e5
+│   │   ├── metro-m4
+│   │   └── same54-xplained-pro
+│   ├── samv7
+│   │   ├── common
+│   │   ├── same70-qmtech
+│   │   ├── same70-xplained
+│   │   └── samv71-xult
+│   ├── stm32
+│   │   ├── axoloti
+│   │   ├── b-g431b-esc1
+│   │   ├── b-g474e-dpow1
+│   │   ├── clicker2-stm32
+│   │   ├── cloudctrl
+│   │   ├── common
+│   │   ├── emw3162
+│   │   ├── et-stm32-stamp
+│   │   ├── fire-stm32v2
+│   │   ├── hymini-stm32v
+│   │   ├── maple
+│   │   ├── mikroe-stm32f4
+│   │   ├── nucleo-f103rb
+│   │   ├── nucleo-f207zg
+│   │   ├── nucleo-f302r8
+│   │   ├── nucleo-f303re
+│   │   ├── nucleo-f303ze
+│   │   ├── nucleo-f334r8
+│   │   ├── nucleo-f410rb
+│   │   ├── nucleo-f412zg
+│   │   ├── nucleo-f429zi
+│   │   ├── nucleo-f446re
+│   │   ├── nucleo-f4x1re
+│   │   ├── nucleo-g431kb
+│   │   ├── nucleo-g431rb
+│   │   ├── nucleo-l152re
+│   │   ├── olimexino-stm32
+│   │   ├── olimex-stm32-e407
+│   │   ├── olimex-stm32-h405
+│   │   ├── olimex-stm32-h407
+│   │   ├── olimex-stm32-p107
+│   │   ├── olimex-stm32-p207
+│   │   ├── olimex-stm32-p407
+│   │   ├── omnibusf4
+│   │   ├── photon
+│   │   ├── shenzhou
+│   │   ├── stm3210e-eval
+│   │   ├── stm3220g-eval
+│   │   ├── stm3240g-eval
+│   │   ├── stm32butterfly2
+│   │   ├── stm32f103-minimum
+│   │   ├── stm32f334-disco
+│   │   ├── stm32f3discovery
+│   │   ├── stm32f411e-disco
+│   │   ├── stm32f411-minimum
+│   │   ├── stm32f429i-disco
+│   │   ├── stm32f4discovery
+│   │   ├── stm32ldiscovery
+│   │   ├── stm32_tiny
+│   │   ├── stm32vldiscovery
+│   │   └── viewtool-stm32f107
+│   ├── stm32f0l0g0
+│   │   ├── b-l072z-lrwan1
+│   │   ├── nucleo-f072rb
+│   │   ├── nucleo-f091rc
+│   │   ├── nucleo-g070rb
+│   │   ├── nucleo-g071rb
+│   │   ├── nucleo-l073rz
+│   │   ├── stm32f051-discovery
+│   │   └── stm32f072-discovery
+│   ├── stm32f7
+│   │   ├── nucleo-144
+│   │   ├── stm32f746g-disco
+│   │   ├── stm32f746-ws
+│   │   └── stm32f769i-disco
+│   ├── stm32h7
+│   │   ├── nucleo-h743zi
+│   │   ├── nucleo-h743zi2
+│   │   └── stm32h747i-disco
+│   ├── stm32l4
+│   │   ├── b-l475e-iot01a
+│   │   ├── nucleo-l432kc
+│   │   ├── nucleo-l452re
+│   │   ├── nucleo-l476rg
+│   │   ├── nucleo-l496zg
+│   │   ├── stm32l476-mdk
+│   │   ├── stm32l476vg-disco
+│   │   └── stm32l4r9ai-disco
+│   ├── stm32l5
+│   │   ├── drivers
+│   │   ├── nucleo-l552ze
+│   │   └── stm32l562e-dk
+│   ├── stm32u5
+│   │   ├── b-u585i-iot02a
+│   │   └── drivers
+│   ├── str71x
+│   │   └── olimex-strp711
+│   ├── tiva
+│   │   ├── dk-tm4c129x
+│   │   ├── eagle100
+│   │   ├── ekk-lm3s9b96
+│   │   ├── launchxl-cc1310
+│   │   ├── launchxl-cc1312r1
+│   │   ├── lm3s6432-s2e
+│   │   ├── lm3s6965-ek
+│   │   ├── lm3s8962-ek
+│   │   ├── lm4f120-launchpad
+│   │   ├── tm4c123g-launchpad
+│   │   └── tm4c1294-launchpad
+│   ├── tms570
+│   │   ├── launchxl-tms57004
+│   │   └── tms570ls31x-usb-kit
+│   └── xmc4
+│       ├── xmc4500-relax
+│       └── xmc4700-relax
+├── avr
+│   ├── at32uc3
+│   │   └── avr32dev1
+│   ├── at90usb
+│   │   ├── micropendous3
+│   │   └── teensy-2.0
+│   └── atmega
+│       ├── amber
+│       ├── arduino-mega2560
+│       └── moteino-mega
+├── dummy
+├── hc
+│   └── m9s12
+│       ├── demo9s12ne64
+│       └── ne64badge
+├── mips
+│   ├── pic32mx
+│   │   ├── mirtoo
+│   │   ├── pic32mx7mmb
+│   │   ├── pic32mx-starterkit
+│   │   ├── sure-pic32mx
+│   │   └── ubw32
+│   └── pic32mz
+│       ├── chipkit-wifire
+│       ├── flipnclick-pic32mz
+│       └── pic32mz-starterkit
+├── misoc
+│   └── lm32
+│       └── misoc
+├── or1k
+│   └── mor1kx
+│       └── or1k
+├── renesas
+│   ├── m16c
+│   │   └── skp16c26
+│   ├── rx65n
+│   │   ├── rx65n
+│   │   ├── rx65n-grrose
+│   │   ├── rx65n-rsk1mb
+│   │   └── rx65n-rsk2mb
+│   └── sh1
+│       └── us7032evb1
+├── risc-v
+│   ├── bl602
+│   │   └── bl602evb
+│   ├── c906
+│   │   └── smartl-c906
+│   ├── esp32c3
+│   │   └── esp32c3-devkit
+│   ├── fe310
+│   │   └── hifive1-revb
+│   ├── k210
+│   │   └── maix-bit
+│   ├── litex
+│   │   └── arty_a7
+│   ├── mpfs
+│   │   ├── common
+│   │   ├── icicle
+│   │   └── m100pfsevp
+│   ├── qemu-rv
+│   │   └── rv-virt
+│   └── rv32m1
+│       └── rv32m1-vega
+├── sim
+│   └── sim
+│       └── sim
+├── sparc
+│   ├── bm3803
+│   │   └── xx3803
+│   └── bm3823
+│       └── xx3823
+├── x86
+│   └── qemu
+│       └── qemu-i486
+├── x86_64
+│   └── intel64
+│       └── qemu-intel64
+├── xtensa
+│   ├── esp32
+│   │   ├── common
+│   │   ├── esp32-devkitc
+│   │   ├── esp32-ethernet-kit
+│   │   ├── esp32-wrover-kit
+│   │   └── ttgo_lora_esp32
+│   ├── esp32s2
+│   │   ├── common
+│   │   └── esp32s2-saola-1
+│   └── esp32s3
+│       ├── common
+│       └── esp32s3-devkit
+├── z16
+│   └── z16f
+│       └── z16f2800100zcog
+└── z80
+    ├── ez80
+    │   ├── ez80f910200kitg
+    │   ├── ez80f910200zco
+    │   ├── makerlisp
+    │   └── z20x
+    ├── z180
+    │   └── p112
+    ├── z8
+    │   ├── z8encore000zco
+    │   └── z8f64200100kit
+    └── z80
+        └── z80sim
+
+337 directories
+
\ No newline at end of file diff --git a/blog/octave-cli/index.html b/blog/octave-cli/index.html new file mode 100644 index 0000000..48c8f0f --- /dev/null +++ b/blog/octave-cli/index.html @@ -0,0 +1,21 @@ +Octave Command Line Mode

Octave Command Line Mode

2022-02-27

GNU Octave has a lesser known command line mode. I think it's awesome. You get to work in the always familiar environment in the shell with vim running alongside to edit script files... You have access to certain shell commands such as cd, ls, mkdir. Enough to navigate you way around. Makes you feel like a true hacker rather than working in the outdated, ugly GUI IDE. Here's a snapshot:

> octave
+GNU Octave, version 6.1.1~hg.2021.01.26
+Copyright (C) 2020 The Octave Project Developers.
+This is free software; see the source code for copying conditions.
+There is ABSOLUTELY NO WARRANTY; not even for MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  For details, type 'warranty'.
+
+Octave was configured for "x86_64-pc-linux-gnu".
+
+Additional information about Octave is available at https://www.octave.org.
+
+Please contribute if you find this software useful.
+For more information, visit https://www.octave.org/get-involved.html
+
+Read https://www.octave.org/bugs.html to learn how to submit bug reports.
+For information about changes from previous versions, type 'news'.
+
+octave:1> pi
+ans = 3.1416
+octave:2> 
+
\ No newline at end of file diff --git a/blog/pico-4m/index.html b/blog/pico-4m/index.html new file mode 100644 index 0000000..a6b73d2 --- /dev/null +++ b/blog/pico-4m/index.html @@ -0,0 +1 @@ +Review: PI Pico 4MB version

Review: PI Pico 4MB version

2022-11-04

Pico 4M versio

I recently tried out the PICO 4MB version sold here: https://www.aliexpress.com/item/1005003928558306. A cheaper alternative to the official version but with the larger flash.

Specifically called "Lite Black" by the seller, it has a similar layout to the original with the single LED at port-25.

Considering it's an Cortex-M chipset (M0+) it will be programmed by a ST-LINK (SWD) debugger probe. The PI Pico comes with it's own programming environment / SDK for building application in C/C++.

This 4MB variant at it's given price is an excellent alternative to the classic bluepill which lacks flash for any complex applications. And will be a drop in replacement for any bluepill suitable projects in future.

Articles about Pico Projects soon to come...

\ No newline at end of file diff --git a/blog/piracy/index.html b/blog/piracy/index.html new file mode 100644 index 0000000..d15e741 --- /dev/null +++ b/blog/piracy/index.html @@ -0,0 +1,69 @@ +Piracy on a Budget

Piracy on a Budget

2022-11-24

The Problem:

  • getting a server with resources for Plex serving capabilities is expensive
  • getting a server with TB storage is ineffective / moot, then when you have a local server
  • running the usual home-based seedbox clogs up your connection by seeding all the time1 (you loose your internet's full potential)

The Solution: make a 'smart' remote seedbox (on a cheap VPS) that queue's the torrents according to this remote box's available space and immediately delete the torrent after completing and transferring to the local machine. Then progress to the next pending torrent. The biggest advantage here is that if you do a batch request of torrents (something I do all the time; eg a movie collection) it'll handle progressively without stuffing up the disk space!

Okay that's seems like a lot to digest, but we will do this through rtorrent's scripting capabilities together with remote mount.

posters

The basic principle is simple. We wil use a remote mount to mount the temporary torrents Download directory to a subdir inside the main media directory. (eg: the torrents directory). Then direct radar to this mounted dir as the torrents download directory. Note that we are installing servarr using docker in our local box. If you need instructions on servarr installation see here.

The most complex bit is the installing services on the remote box. We need to install vsftp and rtorrent. Luckily there are automated scripts to get this done. I suggest using 'swizzin'. Once this is out of the way; configuring vsftp is the hardest part... We will see this first.

vsftp by default runs on the traditional ftp mode, where a separate 'data' connection is made from the server to the client(!) on port 21. We will setup the passive mode; where we don't need to open firewalls on the client (!). Use the following config /etc/vsftpd.conf:

listen=YES
+listen_ipv6=NO
+
+user_config_dir=/etc/vsftpd/users
+pasv_enable=Yes
+pasv_max_port=10100
+pasv_min_port=10090
+pasv_address=192.9.174.154
+local_enable=YES
+write_enable=YES
+force_dot_files=YES
+local_umask=022
+dirmessage_enable=YES
+use_localtime=YES
+xferlog_enable=YES
+#connect_from_port_20=NO
+utf8_filesystem=YES
+require_ssl_reuse=NO
+ssl_enable=NO
+pam_service_name=vsftpd
+secure_chroot_dir=/var/run/vsftpd/empty
+
+#ascii_upload_enable=YES
+#ascii_download_enable=YES
+#ftpd_banner=Welcome to blah FTP service.
+
+#############################################
+#Uncomment these lines to enable FXP support#
+#############################################
+pasv_promiscuous=YES
+port_promiscuous=YES
+
+###################
+#Set a custom port#
+###################
+#listen_port=
+
+

then in the user config directory named after your system username: /etc/vsftpd/users/ubuntu

local_root=/path/to/torrents/dir
+dirlist_enable=YES
+download_enable=YES
+write_enable=YES
+

First do a simple commandline test to test the connection: pftp my-server-ip Next do a manual mount with the command to check: curlftpfs -o allow_other ftp-user:ftp-pass@my-ftp-location.local /mnt/my_ftp/. More info on mounting here

Now that we have the system prepared we just need to tweak the radarr/sonarr settings to reflect our setup.

Now we come to the meat of this article. Swizzin' goes a long way in glueing up the services; which is rutorrent with the download client rtorrent. The rtorrent setup lives in ~/.rtorrent.rc.

However this link betweent the client and the rutorrent GUI is done using unix sockets. And works within the local host. But we intend to access rtorrent remotely from our home server. We need to setup a reverse proxy using the already installed nginx server to route this socket to the outside world onto a network port.

To do this insert the following block inside of the preexisting 'http' block:

# rtorrent XMLRPC
+        server {
+           listen 0.0.0.0:8080;
+           error_log /var/log/nginx/nginx.error.log;
+           access_log /var/log/nginx/access.log;
+
+           auth_basic "Restricted";
+           # Users must log on with matching credentials
+           auth_basic_user_file /etc/nginx/.htpasswd;
+
+           location /RPC2 {
+              include scgi_params;
+              scgi_pass unix:/var/run/ubuntu/.rtorrent.sock;
+           }
+        }
+

Make sure to check the XMLRPC socket path specified in the .rtorrent.rc file. Dont forget to open the firewall port!

Next as indicated we need to provide the auth users list file /etc/nginx/.htpasswd. This is the credentials we will need to provide in radarr connection settings. To do this apply the following commands2: sudo sh -c "echo -n 'sammy:' >> /etc/nginx/.htpasswd" sudo sh -c "openssl passwd -apr1 >> /etc/nginx/.htpasswd" and enter a password at the prompts.

Then comeback to radarr settings -> Download Client settings, select rtorrent and fill this:

Name: rTorrent
+rTorrent host: <your-remote-server-ip>
+Port: 8080
+URL Path: RPC2
+Use SSL: OFF
+Username: <your username>
+Password: <your password>
+Add label to torrent: TV (or anything else you desire)
+Optional - Downloaded files location: <custom directory>
+

The final thing remaining to complete our connection is to map the remote folder as explained here. In our case map the set in the .rtorrent.rc as directory.default.set to the ftp mount directory. The import will occur between these folders after rtorrent has completed the download.

That's it; we can now test a torrent download from radarr! The only downside to our setup is that we dont see the progress when importing; ie. downloading from the torrent box to our local server.

Now we need to script up rtorrent so that it behaves according to our constraints above.

Actually swizzin' gives us a great plugin that is installed by default that takes care of this by itself: QUOTA.

This will manage the disk space issue automatically; just point Quota to the right volume you intend to use for downloads folder when installing swizzin'.

All we need to do now is set the queue length in rutorrent and setup auto-deletion in the .rtorrent.rc file when the ratio is reached. Add this to the file3:

ratio.enable=
+ratio.min.set=200
+method.set = group.seeding.ratio.command, "d.close= ; d.erase="
+

All Done!

\ No newline at end of file diff --git a/blog/postfix-mail/index.html b/blog/postfix-mail/index.html new file mode 100644 index 0000000..a1bcd29 --- /dev/null +++ b/blog/postfix-mail/index.html @@ -0,0 +1,31 @@ +Selfhosted email delivery with postfix

Selfhosted email delivery with postfix

2022-12-27

Note that it is assumed that a Gitlab docker instance is setup on a Debian 10/11 machine... This article follows the post.

According to the docs, the official image of Gitlab doesn't include a Mail Transfer Agent (MTA) preinstalled. For our purposes this is the program that routes out the email from our system. So we'll install it on the host system and setup Gitlab to route mail to it.

This is simple enough, but getting our local hosted email service to play nice with Gmail is a different story.

We begin by setting up basic postfix email. Install postfix as per this. We only need step one.

To test:

echo "Subject: test" | sendmail -v mymail@mydomain.com
+

This will not actually deliver the mail as yet; but atleast it should run without errors!

To allow the docker container to access postfix we make a small change to /etc/postfix/main.cf. Add 0.0.0.0/0 to mynetworks. This allows any IP to initiate email, as IP address change with every container restarts, we will take this approach.

Remember to restart postfix on every edit to this file by:

sudo systemctl reload postfix
+

Then add this smtp settings in our Gitlab's docker-compose.yml:

gitlab_rails['smtp_enable'] = true
+gitlab_rails['smtp_address'] = "gitlab.example.com"
+gitlab_rails['smtp_port'] = 25
+

Note that 'smtp_address' will be the FQDN that we set as our host machine domain.

Now to test, execute the following:

sudo docker exec -it  gitlab-compose-web-1 bash
+gitlab-rails console -e production
+
+Notify.test_email('mymail@mydomain.com', 'Message Subject', 'Message Body').deliver_now
+

In the first command gitlab-compose-web-1 is the name of the container. To check your running name run sudo docker ps.

Note this will not yet work if you're using a Gmail addresses read on to fix this...

Setting up SASL (for Gmail receipients)#

Next thing to do is to install Dovecot. On debian:

sudo apt install dovecot-core dovecot-pop3d dovecot-imapd
+

Then in /etc/dovecot/conf.d/10-master.conf:

service auth {
+  unix_listener auth-userdb {
+    mode = 0666
+    user = postfix
+    group = postfix
+  }
+
+  # Postfix smtp-auth
+  #unix_listener /var/spool/postfix/private/auth {
+  #  mode = 0666
+  #}
+
+  # Auth process is run as this user.
+  #user = $default_internal_user
+}
+

Don't forget to restart dovecot:

sudo systemctl restart dovecot
+

Then modify postfix to use dovecot ADD /etc/postfix/main.cf:

smtpd_sasl_type = dovecot
+

Don't forget to restart postfix:

sudo systemctl reload postfix
+

Lastly we need to add a TXT record for our domain:

v=spf1 ip4:x.x.x.x ~all
+

where x.x.x.x is the IP of the server.

Now try:

echo "Subject: test" | sendmail -v me@gmail.com
+

Tip: handy commands to view the log messages: postfix: sudo tail -f /var/log/mail.log Dovecot: sudo tail -f /var/log/syslog

\ No newline at end of file diff --git a/blog/project-gravity-detector/index.html b/blog/project-gravity-detector/index.html new file mode 100644 index 0000000..bad111d --- /dev/null +++ b/blog/project-gravity-detector/index.html @@ -0,0 +1,415 @@ +Project: Gravity Detector

Project: Gravity Detector

2023-05-06

Here we will hookup a MPU6050 accelerometer/gyro sensor to our micro:bit to detect the orientation of the board.

The microbit comes with an onboard accelerometer; but for the purpose of learning the procedure we will attach an external sensor to the board. The microbit also conveniently features a 5x5 LED Matrix, which we will use as a display to indicate direction. Basically we will imitate the effect of a dot on the display to flow towards the direction of gravity.

Given this is our first proper project with Zephyr, this will serve as an exercise of project bring-up and its structural nature.


We begin by cloning (or copying) the basic hello world project. Which will be our minimal starting point.

Hardware wise we attach the microbit to the breakout board and then attach it and the sensor to the breadboard. Then simply wire as follows: mpu6050 wiring diagram

We will communicate with the sensor via I^2^C bus. The actual driver file for the MPU6050 is already provided with the Zephyr sources! But we will need to provide an overlay file to reflect our custom wiring (which is the main exercise of using an external sensor). And remember to activate the modules in prj.conf.

<zephyr_root>/applications/mpu6050/boards/bbc_microbit_v2.overlay:


+&pinctrl {
+	i2c1_default: i2c1_default {
+		group1 {
+			psels = <NRF_PSEL(TWIM_SDA, 1, 0)>,
+				<NRF_PSEL(TWIM_SCL, 0, 26)>;
+		};
+	};
+
+	i2c1_sleep: i2c1_sleep {
+		group1 {
+			psels = <NRF_PSEL(TWIM_SDA, 1, 0)>,
+				<NRF_PSEL(TWIM_SCL, 0, 26)>;
+			low-power-enable;
+		};
+	};
+};
+
+
+&i2c1 {
+	compatible = "nordic,nrf-twim";
+	status = "okay";
+	clock-frequency = <I2C_BITRATE_FAST>;
+
+	pinctrl-0 = <&i2c1_default>;
+	pinctrl-1 = <&i2c1_sleep>;
+	pinctrl-names = "default", "sleep";
+	/*sda-pin = <34>;
+	scl-pin = <35>;*/
+	mpu6050@68 {
+		compatible = "invensense,mpu6050";
+		reg = <0x68>;
+		status = "okay";
+		int-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
+	};
+};
+

prj.conf:

CONFIG_I2C=y
+CONFIG_SENSOR=y
+CONFIG_MPU6050_TRIGGER_NONE=y
+CONFIG_CBPRINTF_FP_SUPPORT=y
+
+CONFIG_DISPLAY=y
+

The basic overall application structure is as follows:

.
+├── boards
+│   └── bbc_microbit_v2.overlay
+├── CMakeLists.txt
+├── prj.conf
+└── src
+    └── main.c
+
+

Communication with sensor#

In our first iteration we will achieve basic interaction with the sensor module.

Luckily there is a sample application in the Zephyr: <zephyr_root>/zephyr/samples/sensor/mpu6050/ directory from which we will copy the main.c contents:

#include <zephyr/kernel.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/sensor.h>
+#include <stdio.h>
+
+static const char *now_str(void)
+{
+	static char buf[16]; /* ...HH:MM:SS.MMM */
+	uint32_t now = k_uptime_get_32();
+	unsigned int ms = now % MSEC_PER_SEC;
+	unsigned int s;
+	unsigned int min;
+	unsigned int h;
+
+	now /= MSEC_PER_SEC;
+	s = now % 60U;
+	now /= 60U;
+	min = now % 60U;
+	now /= 60U;
+	h = now;
+
+	snprintf(buf, sizeof(buf), "%u:%02u:%02u.%03u",
+		 h, min, s, ms);
+	return buf;
+}
+
+static int process_mpu6050(const struct device *dev)
+{
+	struct sensor_value temperature;
+	struct sensor_value accel[3];
+	struct sensor_value gyro[3];
+	int rc = sensor_sample_fetch(dev);
+
+	if (rc == 0) {
+		rc = sensor_channel_get(dev, SENSOR_CHAN_ACCEL_XYZ,
+					accel);
+	}
+	if (rc == 0) {
+		rc = sensor_channel_get(dev, SENSOR_CHAN_GYRO_XYZ,
+					gyro);
+	}
+	if (rc == 0) {
+		rc = sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP,
+					&temperature);
+	}
+	if (rc == 0) {
+		printf("[%s]:%g Cel\n"
+		       "  accel %f %f %f m/s/s\n"
+		       "  gyro  %f %f %f rad/s\n",
+		       now_str(),
+		       sensor_value_to_double(&temperature),
+		       sensor_value_to_double(&accel[0]),
+		       sensor_value_to_double(&accel[1]),
+		       sensor_value_to_double(&accel[2]),
+		       sensor_value_to_double(&gyro[0]),
+		       sensor_value_to_double(&gyro[1]),
+		       sensor_value_to_double(&gyro[2]));
+	} else {
+		printf("sample fetch/get failed: %d\n", rc);
+	}
+
+	return rc;
+}
+
+#ifdef CONFIG_MPU6050_TRIGGER
+static struct sensor_trigger trigger;
+
+static void handle_mpu6050_drdy(const struct device *dev,
+				const struct sensor_trigger *trig)
+{
+	int rc = process_mpu6050(dev);
+
+	if (rc != 0) {
+		printf("cancelling trigger due to failure: %d\n", rc);
+		(void)sensor_trigger_set(dev, trig, NULL);
+		return;
+	}
+}
+#endif /* CONFIG_MPU6050_TRIGGER */
+
+int main(void)
+{
+	const struct device *const mpu6050 = DEVICE_DT_GET_ONE(invensense_mpu6050);
+
+	if (!device_is_ready(mpu6050)) {
+		printf("Device %s is not ready\n", mpu6050->name);
+		return 0;
+	}
+
+#ifdef CONFIG_MPU6050_TRIGGER
+	trigger = (struct sensor_trigger) {
+		.type = SENSOR_TRIG_DATA_READY,
+		.chan = SENSOR_CHAN_ALL,
+	};
+	if (sensor_trigger_set(mpu6050, &trigger,
+			       handle_mpu6050_drdy) < 0) {
+		printf("Cannot configure trigger\n");
+		return 0;
+	}
+	printk("Configured for triggered sampling.\n");
+#endif
+
+	while (!IS_ENABLED(CONFIG_MPU6050_TRIGGER)) {
+		int rc = process_mpu6050(mpu6050);
+
+		if (rc != 0) {
+			break;
+		}
+		k_sleep(K_SECONDS(2));
+	}
+
+	/* triggered runs with its own thread after exit */
+	return 0;
+}
+

The output on the Zephyr serial console:

*** Booting Zephyr OS build zephyr-v3.3.0-3014-ge59e65dc75e4 ***
+[0:00:00.008]:19.9182 Cel
+  accel 0.351947 -0.275334 10.371681 m/s/s
+  gyro  -0.021583 -0.014655 -0.006794 rad/s
+[0:00:02.019]:19.8712 Cel
+  accel 0.272938 -0.150835 10.292672 m/s/s
+  gyro  -0.019584 -0.009592 -0.012124 rad/s
+

Output via LED Matrix#

Now that we have confirmed successful communication with the module, we will strip off code related to console logging. And introduce the following function:

void update_mydisplay(double y, double z, const struct device *dev)
+{
+	// makeup buf here
+/*	buf[0] = PIXEL_MASK(1, 0, 1, 0, 1);
+	buf[1] = PIXEL_MASK(1, 1, 0, 0, 1);
+	buf[2] = PIXEL_MASK(1, 0, 1, 0, 1);
+	buf[3] = PIXEL_MASK(1, 0, 0, 1, 1);
+	buf[4] = PIXEL_MASK(1, 0, 1, 0, 1);
+*/
+	uint8_t tbuf[5][5];
+	int i, j, ii, jj, ret;
+
+	for (i = 0; i < 5; i++)
+		for (j = 0; j < 5; j++)
+			tbuf[i][j] = 0;
+
+	// work the dot here
+	jj = 0 - y/10*5 + 2;
+	ii = z/10*5 + 2;
+
+	// assign ceiling values to trap the dot into display!
+	if (ii > 4)
+		ii = 4;
+	if (jj > 4)
+		jj = 4;
+	if (ii < 0)
+		ii = 0;
+	if (jj < 0)
+		jj = 0;
+
+	tbuf[ii][jj] = 1;
+	for (i = 0; i < 5; i++)
+		buf[i] = 0;
+	
+	for (i = 0; i < 5; i++)
+		for (j = 0; j < 5; j++)
+		{
+			buf[i] |= tbuf[i][j];
+			if (j < 4)
+				buf[i] <<= 1;
+		}
+
+	ret = display_set_brightness(dev, 0x7F);
+	if (ret < 0) {
+		printk("display_set_brightness failed: %u/%d\n",
+			__LINE__, ret);
+	}
+	ret = display_write(dev, 0, 0, &buf_desc, buf);
+	if (ret < 0) {
+		printk("display_write failed: %u/%d\n",
+			__LINE__, ret);
+	}
+
+	ret = display_blanking_off(dev);
+	if (ret < 0) {
+		printk("display_blanking_off failed: %u/%d\n",
+			__LINE__, ret);
+	}
+
+}
+

In the code above, we use an intermediate 2d array to represent the LED Matrix to flexibly refer to our indicating dot. This dot will move towards the direction of flowing gravity. Now our sensor is 3-dimensional but we only consider 2 axes for the 2d array. By means of trial-and-error, this works out to be the x and z axis of the sensor, which is passed to this function.

Since our final LED Matrix data is single dimensional 5bit array; we need to populate this data by means of our temporary 2d array. We do this by bitwise shifting to the left while cycling through each row and copy it through.

And we need to periodically call this function after processing sensor data. Here's the updated main.c:

#include <zephyr/kernel.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/display.h>
+#include <zephyr/drivers/sensor.h>
+#include <stdio.h>
+
+#define PIXEL_BIT(idx, val)  (val ? BIT(idx) : 0)
+#define PIXEL_MASK(...)      (FOR_EACH_IDX(PIXEL_BIT, (|), __VA_ARGS__))
+
+static struct display_capabilities caps;
+static uint8_t buf[5];
+static const struct display_buffer_descriptor buf_desc = {
+	.buf_size = sizeof(buf),
+	.width    = 5,
+	.height   = 5,
+	.pitch    = 8,
+};
+
+void update_mydisplay(double y, double z, const struct device *dev)
+{
+	// makeup buf here
+/*	buf[0] = PIXEL_MASK(1, 0, 1, 0, 1);
+	buf[1] = PIXEL_MASK(1, 1, 0, 0, 1);
+	buf[2] = PIXEL_MASK(1, 0, 1, 0, 1);
+	buf[3] = PIXEL_MASK(1, 0, 0, 1, 1);
+	buf[4] = PIXEL_MASK(1, 0, 1, 0, 1);
+*/
+	uint8_t tbuf[5][5];
+	int i, j, ii, jj, ret;
+
+	for (i = 0; i < 5; i++)
+		for (j = 0; j < 5; j++)
+			tbuf[i][j] = 0;
+
+	// work the dot here
+	jj = 0 - y/10*5 + 2;
+	ii = z/10*5 + 2;
+
+	// assign ceiling values to trap the dot into display!
+	if (ii > 4)
+		ii = 4;
+	if (jj > 4)
+		jj = 4;
+	if (ii < 0)
+		ii = 0;
+	if (jj < 0)
+		jj = 0;
+
+	tbuf[ii][jj] = 1;
+	for (i = 0; i < 5; i++)
+		buf[i] = 0;
+	
+	for (i = 0; i < 5; i++)
+		for (j = 0; j < 5; j++)
+		{
+			buf[i] |= tbuf[i][j];
+			if (j < 4)
+				buf[i] <<= 1;
+		}
+
+	ret = display_set_brightness(dev, 0x7F);
+	if (ret < 0) {
+		printk("display_set_brightness failed: %u/%d\n",
+			__LINE__, ret);
+	}
+	ret = display_write(dev, 0, 0, &buf_desc, buf);
+	if (ret < 0) {
+		printk("display_write failed: %u/%d\n",
+			__LINE__, ret);
+	}
+
+	ret = display_blanking_off(dev);
+	if (ret < 0) {
+		printk("display_blanking_off failed: %u/%d\n",
+			__LINE__, ret);
+	}
+
+}
+
+static const char *now_str(void)
+{
+	static char buf[16]; /* ...HH:MM:SS.MMM */
+	uint32_t now = k_uptime_get_32();
+	unsigned int ms = now % MSEC_PER_SEC;
+	unsigned int s;
+	unsigned int min;
+	unsigned int h;
+
+	now /= MSEC_PER_SEC;
+	s = now % 60U;
+	now /= 60U;
+	min = now % 60U;
+	now /= 60U;
+	h = now;
+
+	snprintf(buf, sizeof(buf), "%u:%02u:%02u.%03u",
+		 h, min, s, ms);
+	return buf;
+}
+
+static int process_mpu6050(const struct device *dev, const struct device *ddev)
+{
+	struct sensor_value temperature;
+	struct sensor_value accel[3];
+	struct sensor_value gyro[3];
+	int rc = sensor_sample_fetch(dev);
+
+	if (rc == 0) {
+		rc = sensor_channel_get(dev, SENSOR_CHAN_ACCEL_XYZ,
+					accel);
+	}
+	if (rc == 0) {
+		rc = sensor_channel_get(dev, SENSOR_CHAN_GYRO_XYZ,
+					gyro);
+	}
+	if (rc == 0) {
+		rc = sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP,
+					&temperature);
+	}
+	if (rc == 0) {
+/*		printf("[%s]:%g Cel\n"
+		       "  accel %f %f %f m/s/s\n"
+		       "  gyro  %f %f %f rad/s\n",
+		       now_str(),
+		       sensor_value_to_double(&temperature),
+		       sensor_value_to_double(&accel[0]),
+		       sensor_value_to_double(&accel[1]),
+		       sensor_value_to_double(&accel[2]),
+		       sensor_value_to_double(&gyro[0]),
+		       sensor_value_to_double(&gyro[1]),
+		       sensor_value_to_double(&gyro[2]));*/
+	} else {
+		printf("sample fetch/get failed: %d\n", rc);
+	}
+
+	update_mydisplay(sensor_value_to_double(&accel[0]), sensor_value_to_double(&accel[2]), ddev);
+
+	return rc;
+}
+
+
+int main(void)
+{
+	const struct device *const mpu6050 = DEVICE_DT_GET_ONE(invensense_mpu6050);
+
+	if (!device_is_ready(mpu6050)) {
+		printf("Device %s is not ready\n", mpu6050->name);
+		return 0;
+	}
+
+	int ret;
+	const struct device *const dev = DEVICE_DT_GET_ONE(nordic_nrf_led_matrix);
+
+	if (!dev) {
+		printk("Display device not ready\n");
+		return 0;
+	}
+
+	display_get_capabilities(dev, &caps);
+	if (!(caps.supported_pixel_formats & PIXEL_FORMAT_MONO01)) {
+		printk("Expected pixel format not supported\n");
+		return 0;
+	}
+
+	ret = display_set_pixel_format(dev, PIXEL_FORMAT_MONO01);
+	if (ret < 0) {
+		printk("display_set_pixel_format failed: %u/%d\n",
+			__LINE__, ret);
+	}
+
+	while (!IS_ENABLED(CONFIG_MPU6050_TRIGGER)) {
+		int rc = process_mpu6050(mpu6050, dev);
+
+		if (rc != 0) {
+			break;
+		}
+		k_msleep(100);
+	}
+
+	/* triggered runs with its own thread after exit */
+	return 0;
+}
+
+

animation final effect

\ No newline at end of file diff --git a/blog/pwm/index.html b/blog/pwm/index.html new file mode 100644 index 0000000..e28bc82 --- /dev/null +++ b/blog/pwm/index.html @@ -0,0 +1 @@ +PWM

PWM

2022-02-24

Led Fade

Step 1: Led Fade#

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit ipsum, bibendum eget semper ac, eleifend sit amet ex. In hac habitasse platea dictumst. Morbi tempus metus a nibh blandit condimentum. Quisque quis neque urna. Etiam eget sapien ac lacus accumsan tincidunt nec nec felis. Quisque placerat, justo vitae congue efficitur, est est volutpat magna, sit amet consectetur purus lorem sed neque. Ut aliquet elit a ultrices hendrerit. In hac habitasse platea dictumst. Pellentesque quis eros lacinia, porttitor justo a, suscipit erat. Etiam vestibulum nibh quis mattis laoreet. Aenean sed lacus in massa elementum dapibus. Maecenas at arcu condimentum, consectetur ipsum a, dignissim nibh. Nulla facilisi. Suspendisse potenti. Aliquam eleifend ultrices gravida. Proin lacinia pellentesque faucibus.

Led Fade with DMA & Interrupts (Idle CPU)#

< TODO >

\ No newline at end of file diff --git a/blog/riot-oled/index.html b/blog/riot-oled/index.html new file mode 100644 index 0000000..2bca293 --- /dev/null +++ b/blog/riot-oled/index.html @@ -0,0 +1,32 @@ +First project in RIOT-OS: OLED

First project in RIOT-OS: OLED

2023-11-10

Today, we're diving into a nifty project that's as exciting as it is educational. We're going to use the STM32F103C8 "Blue Pill" microcontroller and Riot-OS to drive an SSD1306 I2C OLED module – and trust me, it's going to be a blast!

First off, for those new to the playground, let's give a quick intro to our main characters. The STM32F103C8, affectionately known as the "Blue Pill," is a seriously cost-effective microcontroller that packs a punch with an ARM Cortex-M3 processor. Then there's Riot-OS, an open-source operating system that's optimized for IoT devices. It's like the life of the party for microcontrollers, giving them the software needed to interact with the world. And the SSD1306 is a tiny OLED display that’s so easy to get along with; it communicates over I2C, which means we can have a nice little chat with just a couple of wires.

So, let's get to the meat and potatoes.

Materials Needed:

  • STM32F103C8 "Blue Pill" microcontroller
  • SSD1306 I2C OLED display
  • Connecting wires
  • USB to Serial converter for programming

Step 1: Setting Up the Environment Before we start, ensure you have a programming environment set up for the STM32F103C8. You'll need the ARM GCC compiler and OpenOCD for flashing. For Riot-OS, clone the repository from GitHub and get the build environment ready.

Step 2: Wiring Up Connect your SSD1306 display to the Blue Pill. The display needs four connections:

  • VCC to 3.3V
  • GND to GND
  • SCL to B6 (or whichever I2C clock pin you prefer)
  • SDA to B7 (or your chosen I2C data pin)

Step 3: Bootstrapping Riot-OS Navigate to the Riot-OS base directory and create a new project for our Blue Pill. You can find a board configuration for the Blue Pill in Riot's boards directory if you don't feel like starting from scratch.

Step 4: Writing The Code Here's a simple skeleton to start with in your main.c:

#include <stdio.h>
+#include "xtimer.h"
+#include "periph_conf.h"
+#include "periph/i2c.h"
+#include "ssd1306.h"
+#include "ssd1306_params.h"
+
+int main(void)
+{
+    puts("STM32 + SSD1306 OLED screen with RIOT");
+
+    /* Initialize the OLED display */
+    ssd1306_t dev;
+    if (ssd1306_init(&dev, &ssd1306_params[0]) != 0) {
+        puts("Initialization failed");
+        return 1;
+    }
+    ssd1306_set_contrast(&dev, 0xFF); // Max contrast
+    ssd1306_display_on(&dev, true);
+
+    /* Display some text on the screen */
+    ssd1306_write_string(&dev, "Hello, IoT World!");
+
+    while (1) {
+        /* You could add some code here to update the display */
+        xtimer_sleep(1);
+    }
+
+    return 0;
+}
+

Step 5: Building and Flashing To compile and flash your code to the Blue Pill, use the following commands from your project's directory:

make BOARD=bluepill flash
+

Assuming all goes well, you should see your "Hello, IoT World!" message on the display.

Printout OLED result

Step 6: Experimenting Now that you've got the basics down, why not play around with the display? Riot-OS has a rich set of libraries for graphics, so you can draw shapes, display sensor data, or even create a mini game on your OLED.

Step 7: Debugging If you run into any issues, remember: debugging is half the fun in embedded systems. Check your connections, ensure the I2C addresses match, and use the make term command to peek into the logs.

Wrapping Up You've just made a microcontroller communicate with an OLED display, and that's pretty darn cool. The Blue Pill and Riot-OS are like peanut butter and jelly – they just work beautifully together.

Remember, the key to mastery is experimentation. So, tweak the contrast, flip the display, scroll text, and see what your Blue Pill can do. With the SSD1306, your Blue Pill isn’t just crunching numbers; it’s showing off its results with style.

And there you have it, folks – a simple, yet thrilling journey through embedded development with a dash of IoT. Keep tinkering, keep learning, and most importantly, keep having fun. Until next time, may your connections be stable and your code bug-free!

\ No newline at end of file diff --git a/blog/selfhosted-gitlab/index.html b/blog/selfhosted-gitlab/index.html new file mode 100644 index 0000000..0f5b2eb --- /dev/null +++ b/blog/selfhosted-gitlab/index.html @@ -0,0 +1,32 @@ +Gitlab Selfhohsted!

Gitlab Selfhohsted!

2022-11-23

Carrying on my spree of selfhosted software, I moved to hosting my on instance of the free and opensource ==Gitlab==. I think this will be the most rewarding selfhosted project given it's useful nature.

To peek on my installed instance goto http://gitlab.myjabber.site/.

The good news is that it only requires a cheap less powerful instance. On the flip side we need 2 of those! A dedicated one is recommended for the CI/CD runner application, for acceptable performance. Or it may hurt the responsiveness.

And it was a convenient time to hustle a deal on VPS with black friday on! As being cost conscious as ever; I headed to Lowendbox to snap a deal. At the time I checked Racknerd had a 2.5G deal for 23.50usd yearly. And then for the Gitlab Runner instance, I opted for the CloudServer's 1G for 10usd/yearly. It takes a lot of time to compile the pipiline on some projects but it'd fine for my usecase...

I initially rushed in for a native binary install on the Debian 11 machine I setup. Then I reset the VM and went for the docker based setup, which is just straightforward. Once this is done, you have to download the binary for the runner onto the secondary VM and set it up to attach to the main instance. You will find instructions to do this on the gitlab's documention website.

For those interested here's a copy of my docker-compose.yml.

version: '3.6'
+services:
+  web:
+    image: 'gitlab/gitlab-ee:latest'
+    restart: always
+    hostname: 'gitlab'
+    environment:
+      GITLAB_OMNIBUS_CONFIG: |
+        external_url 'https://gitlab.myjabber.site'
+        gitlab_rails['gitlab_shell_ssh_port'] = 2289
+        # Add any other gitlab.rb configuration here, each on its own line
+        pages_external_url "http://pages.myjabber.site" # not a subdomain of external_url
+        #pages_nginx['redirect_http_to_https'] = true
+        gitlab_rails['smtp_enable'] = true
+        gitlab_rails['smtp_address'] = "smtp.mailgun.org"
+        gitlab_rails['smtp_port'] = 587
+        gitlab_rails['smtp_authentication'] = "plain"
+        gitlab_rails['smtp_enable_starttls_auto'] = true
+        gitlab_rails['smtp_user_name'] = "<from address>"
+        gitlab_rails['smtp_password'] = "<password_here>"
+        gitlab_rails['smtp_domain'] = "<mailgun domain>"
+    ports:
+      - '80:80'
+      - '443:443'
+      - '2289:22'
+    volumes:
+      - '$GITLAB_HOME/config:/etc/gitlab'
+      - '$GITLAB_HOME/logs:/var/log/gitlab'
+      - '$GITLAB_HOME/data:/var/opt/gitlab'
+    shm_size: '256m'
+

and the .env file in the same folder:

GITLAB_HOME=/srv/gitlab
+

You may have noticed the use of smtp settings. This is used to mail the users with notifications. I've used Mailgun as my email forwarder; which I think is the goto these days. You can send 2000 mails in the free tier. The free tier has been stopped! Check this for local email forwarder.

Note: I had trouble setting up Gitlab Pages to work on my instance, this may have been because of SSL certificates improperly installed. But I gave-up on this issue and commented out the lines above relating to Pages. I suppose it's not that important on a self hosted instance!?

Finally don't forget to setup the firewall!!

  • A note on backup: Given the sensitive nature of one's own code repository it is a no brainer to setup backups. You could use the Oracle's free tier compute VPS for this purpose. We will spinup the AMD [] instead of the ARM Ampere since we dont need resources for this purpose.

Proceed to install rsync on both machines and follow with the script provided here.

\ No newline at end of file diff --git a/blog/serial/index.html b/blog/serial/index.html new file mode 100644 index 0000000..337a080 --- /dev/null +++ b/blog/serial/index.html @@ -0,0 +1,22 @@ +Serial UART

Serial UART

2022-02-24

We will study the UART peripheral here. First the basic polling mode.

#include <stdio.h>
+#include <hosal_uart.h>
+#include <FreeRTOS.h>
+#include <task.h>
+
+//hosal_uart_dev_t uart_dev;
+
+HOSAL_UART_DEV_DECL(uart_dev, 0, 16, 7, 2000000);
+
+void main (void)
+{
+   char data[] = "test\n";
+
+   // uart setup (for info)
+   hosal_uart_init(&uart_dev);
+
+   while (1) {
+      hosal_uart_send(&uart_dev, data, sizeof(data));
+      vTaskDelay(500);
+   }
+}
+

We begin by including the relevant header hosal_uart.h. The initialisation is done in 2 parts. In line 8, we use the macro to configure the settings as desired, including which uart peripheral we select in the second argument, here we select the uart0. Next we simply call in line 16.

Interrupt Mode#

< todo >

\ No newline at end of file diff --git a/blog/sipeed-bl702/index.html b/blog/sipeed-bl702/index.html new file mode 100644 index 0000000..91aecbd --- /dev/null +++ b/blog/sipeed-bl702/index.html @@ -0,0 +1 @@ +Sipeed BL702 board

Sipeed BL702 board

2022-12-01

A well known Chinese kits maker Sipeed, has started selling a micro board based on the BL702 chip! With some additional peripherals such as an accelerometer and a pixie LED, the board retails for only 4USD. More information can be found here. It even has the 4 debug pins collated together on one side! Good ergonomics!

Main board with debugger

This release from Sipeed paves way for more exposure and popularity which brings more adoption... And in turn more polished toolset and kits. Looking back into the bl_mcu_sdk after some months, there seems to have been an overhaul on the API. Although I'm against making changes (and breaking user's codebase), hopefully this brings better convenence and features!

Considering that the ideal target application is battery powered uses, the board does come with a port for battery input. But it lacks capability for LiPO charging. But this could be easily fixed with an external module such as this.

Happy Hacking!

\ No newline at end of file diff --git a/blog/sphinx/index.html b/blog/sphinx/index.html new file mode 100644 index 0000000..59e8361 --- /dev/null +++ b/blog/sphinx/index.html @@ -0,0 +1,18 @@ +Sphinx Documentation Generator

Sphinx Documentation Generator

2022-12-07

Today I write about the python documentation generator; Sphinx. Which is based on reStructuredText.

reStructuredText is an alternative to Markdown in styling text fragments using simple relevant syntax. However restructured text (or reST for short) is bit more versatile and in my opinion more featured. It is also extensible using third party plugins! One notable feature is the native ability to handle LaTex; which is great for including Mathematic formulas.

In comparison with Markdown, reST aims to be more readable and less obtrusive. For example link addresses are provided with reference as opposed to being inline as in Markdown. The second method is also possible as I said it being versatile.

I intend to document my ideas about RISC-V lowlevel concepts in Sphinx; I believe this will be an interesting take.

One of the initial difficulty in grasping Sphinx is understanding the toctree for linking the table of contents. Which I will clarify here...

Basically it is a recursive link to the other page as a relative reference from the current page's current directory. Once you have generated the base skeleton project from the sphinx-quickstart command; you just need to append these links to other pages in the toc section in index.rst. Like so:

index.rst:

.. toctree::
+   :maxdepth: 4
+   :caption: Contents:
+
+   fm/welcome
+

And then in ./fm/welcome.rst for example:


+=================
+Test welcome page
+=================
+
+This is the first proper page we will write. This should show up as the first page from the TOC!
+
+.. toctree::
+   :maxdepth: 4
+
+   test-page_1
+   test-page_2
+

The above welcome page contains reference to test-page_1.rst and test-page_2.rst which we will need to provide inside the ./fm/ subdir.

As is apparent you can structure your book directory into other subfolders as ./ch1/ and so on.

The Sphinx documentation can be found here and the reST syntax is here.

Happy Writing!

\ No newline at end of file diff --git a/blog/ssd1306/index.html b/blog/ssd1306/index.html new file mode 100644 index 0000000..03073e8 --- /dev/null +++ b/blog/ssd1306/index.html @@ -0,0 +1 @@ +SSD1306 OLED Display

SSD1306 OLED Display

2022-03-17

The SSD1306 128x64 oled display is a popular I/O device that can be added to any project for example to provide some visual diagnostics. It comes in two main variaties; the I2C and the SPI version. We'll be using the I2C one.

SSD1306 128x64

Fortunately the SDK has already the implementation for the lvgl graphics library (provides APIs to simplify access to the display), so we just need to write the I2C protocol driver for oled.

\ No newline at end of file diff --git a/blog/ssg-comparison/index.html b/blog/ssg-comparison/index.html new file mode 100644 index 0000000..ac3d4cb --- /dev/null +++ b/blog/ssg-comparison/index.html @@ -0,0 +1 @@ +Comparison of SSG frameworks

Comparison of SSG frameworks

2023-11-05

Static site generators (SSGs) have gained popularity in recent years as a way to create fast, secure, and easily maintainable websites. In this article, we will compare some popular static site generator software options, highlighting their features, use cases, and benefits.

What is a Static Site Generator?#

A static site generator is a tool that takes content, typically in the form of text files or data, and templates, and generates a complete website composed of HTML, CSS, and JavaScript files. Unlike dynamic websites, static sites do not rely on server-side processing or a database to generate pages. This simplicity results in faster loading times, better security, and easy scalability.

1. Jekyll#

Key Features:

  • Written in Ruby.
  • Great for personal blogs and simple websites.
  • Extensible through plugins.
  • GitHub Pages integration.
  • Markdown and Liquid templating support.

Pros:

  • Straightforward to set up.
  • Ideal for bloggers and developers familiar with Ruby.
  • GitHub Pages, Git integration.
  • Large community and extensive documentation.

Cons:

  • Limited support for complex, database-driven sites.

2. Hugo#

Key Features:

  • Written in Go.
  • Blazing fast build times.
  • Highly customizable.
  • Supports content in multiple formats.
  • Theming system for design flexibility.

Pros:

  • Exceptional speed in building sites.
  • Robust theming and templating.
  • Ideal for technical documentation and blogs.
  • Active and growing community.

Cons:

  • Limited dynamic functionality without external services.

3. Gatsby#

Key Features:

  • React-based.
  • Rich plugin ecosystem.
  • Integrates with various data sources.
  • Progressive Web App (PWA) support.
  • Excellent performance and SEO capabilities.

Pros:

  • Combines the power of React with static site generation.
  • Versatile, can be used for a wide range of projects.
  • Extensive plugin library.
  • SEO-friendly and fast load times.
  • Real-time data updates using GraphQL.

Cons:

  • Steeper learning curve for beginners.

4. Hexo#

Key Features:

  • Written in Node.js.
  • Great for bloggers and developers.
  • Easy theming and plugin system.
  • Markdown and EJS templating support.
  • Speedy build times.

Pros:

  • Simple and quick to set up.
  • Node.js ecosystem and npm packages.
  • Ideal for personal blogs.
  • Support for deploying to various hosting platforms.

Cons:

  • Smaller community compared to some other options.

5. Eleventy (11ty)#

Key Features:

  • Written in JavaScript.
  • Highly flexible and configurable.
  • Supports multiple template languages.
  • Easy integration with various data sources.
  • Robust performance.

Pros:

  • No boilerplate code, minimal configuration.
  • Versatile, suitable for many types of projects.
  • Comprehensive documentation.
  • Strong performance and flexibility.

Cons:

  • Smaller community compared to older SSGs.

Choosing the Right SSG#

The choice of the best static site generator depends on your project's requirements and your familiarity with the underlying technologies. Each SSG has its unique strengths, and you should consider factors like build speed, content sources, theming capabilities, and the size of the community when making your decision.

This site as pointed out before uses the Zola SSG framework. Which was separately reviewed in this post.

In conclusion, static site generators offer a compelling solution for web developers looking to build fast, secure, and easily maintainable websites. Whether you're a blogger, technical writer, or a developer working on a project, there's an SSG that can fit your needs. Choose the one that aligns with your project's goals, and you'll enjoy the benefits of static site generation.

Besides writing in markdown, with git commiting, gives a streamlined blogging experience, where you get to solely focus on the content. And on the other hand, this ecosystem of tools you use, makes you feel like a true hacker which adds to the experience and promotes delivering more content. Which for me personally has been a key motivating factor to maintain this blog.

\ No newline at end of file diff --git a/blog/thoughts-esp-idf/index.html b/blog/thoughts-esp-idf/index.html new file mode 100644 index 0000000..1715ac3 --- /dev/null +++ b/blog/thoughts-esp-idf/index.html @@ -0,0 +1 @@ +Thoughts on the ESP-IDF SDK

Thoughts on the ESP-IDF SDK

2022-03-19

By contrast the esp-idf SDK feels intuitive and very matured. This is a given, considering the popularity of the ESP chips. My only gripe about this ecosystem is the lack of lowpower wireless module. Although this is being fulfilled with the advent of the upcoming ESP32-H2, it's a long overdue wait!

Even though the existing chips feature a BLE radio; it's not as power efficient as it should be, negating any possibility of deploying into low-power applications.

I did consider the well know nRF52840 chip targetting low power wireless applications. A huge advantage here is that it's already available! But most dev-boards, including simple ones like Adafruit Feather and Arduino Nano 33 BLE go for a whopping 40-50usd! Even the company's own (hideous looking) nRF52 dongle is 15usd. Which in my view makes it inaccessible for hobbyist applications.

In the end, this allows me to trial out the esp-idf SDK with the on-hand chip: esp32-c3, while waiting for the H2.

I personally think this is a shameful situation in the maker world in this day and age. Or I might be missing something.

\ No newline at end of file diff --git a/blog/timers/index.html b/blog/timers/index.html new file mode 100644 index 0000000..6476c4d --- /dev/null +++ b/blog/timers/index.html @@ -0,0 +1,9 @@ +Timers

Timers

2022-02-24

Tick Timing#

Basic timing, there is nothing much to it. We just need a couple of functions for basic timing. If you are familiar with tick based timing in another RTOS, it's exactly the same.

tickType ts, milliseconds_spent;
+
+ts = xTaskGetTickCount();
+
+< do stuff >
+
+milliseconds_spent = (xTaskGetTickCount() - ts) / portTICK_RATE_MS;
+
+

Hardware Timers with Interrupts#

\ No newline at end of file diff --git a/blog/welcome/index.html b/blog/welcome/index.html new file mode 100644 index 0000000..54a365c --- /dev/null +++ b/blog/welcome/index.html @@ -0,0 +1 @@ +Welcome

Welcome

2022-02-16

Hello and Welcome!

In this blog we are interested in all things embedded! Primarily the topics will revolve around the RT-Thread RTOS and cheap parts from outlets such as AliExpress.

\ No newline at end of file diff --git a/blog/x99-motherboards/index.html b/blog/x99-motherboards/index.html new file mode 100644 index 0000000..79ef6a8 --- /dev/null +++ b/blog/x99-motherboards/index.html @@ -0,0 +1 @@ +Building a Frugal Gaming PC

Building a Frugal Gaming PC

2023-11-08

Motherboards: The Refurbished x99 AliExpress Special

Here we will talk about a new obsession of mine - refurbished LGA 2011v3 socket motherboards; paired up with used Intel E5 1600/2700 series processor forms the basis for a cheap gaming system. These are sold in AliExpress as a [motherboard, CPU, RAM] combo for upwards of USD80 depending on the manufacturer.

There's good information about these boards on the Russian site: https://xeon-e5450.ru/. Just use Translate Web Pages plugin for your browser to auto-translate.

There's also the Miyconst Youtube Channel that provides good information.

When I purchased, I personally went for the QIYIDA AliExpress seller (the product had tons of good reviews). And received the x99h v1.41 board. According to Miyconst this is a poor board. Mainly due to having apparently only single DDR RAM channel.

I also noticed that the motherboard doesn't support Suspend functionality. In the AHCI options in BIOS, Suspend value is permanently set to Disabled.

The one I purchased came with the following combo:

  • x99h v1.41 board (QIYIDA)
  • Intel Xeon E5 2670v3 processor (used)
  • 16G (2x 8G) RAM QIYIDA brand (refurbished)

It was priced at $110 on Jan 2023.

From my personal experience, I'd advice to do thorough checks (using the sources provided above) and not just rely on AliExpress rankings/ratings. I regret my purchase as I cannot put my PC to sleep (a functionality I heavily rely on).

The whole ecosystem is possible because of the enormous market of the used Xeon chips. The 2670v3 sells on Ali for a grand total of $7! But I recommend buying as a combo for a piece of mind due to any compatiblity issues.

Last comes our most important component: A Graphics card. Ali has us covered in this regard as well! For around $80 you can get a Radeon RX580 8G! Unbeatable! I believe you can go with the AliExpress reviews on this one.

On the bright side though (going with the Xeon 2670v3 processor, which has 12 cores 24 threads), it's refreshing to see the System Monitor report the 24 threads available on the system!

System Monitor screenshot

\ No newline at end of file diff --git a/blog/xt-zb1-bl702/index.html b/blog/xt-zb1-bl702/index.html new file mode 100644 index 0000000..b03a3ac --- /dev/null +++ b/blog/xt-zb1-bl702/index.html @@ -0,0 +1 @@ +THE Dev board you've been waiting for: $1.8 XT-ZB1 Zigbee & BLE devkit features BL702 RISC-V module

THE Dev board you've been waiting for: $1.8 XT-ZB1 Zigbee & BLE devkit features BL702 RISC-V module

2022-02-20

I chanced upon the XT-ZB1 on ali-express on the lookout for the perfect IoT board... Coming at around 1.8usd this is more than perfect, this is a surprise! Made by the same company that makes the SOC for the DT-BL10, Bouffalo Labs, I think this is a new frontier in IoT development. I think we probably should thank the RISC-V movement for making such things possible.

XT-ZB1 pinouts

The MCU specs at: BL702C 32-bit RISC-V microcontroller @ 144 MHz with FPU, 132KB RAM, 192KB ROM with 8MB embedded flash. That's decent enough for most of demanding applications.

There's one caveat however, the zigbee supporting SDK variant is buried in the indicated site: www.bl602.fun, one has to download the tar copy of the SDK there.

Debugging is achieved using the advertised Sipeed RV-Debugger also available on Aliexpress. Alternatively one may use the OpenOCD's ft232r technique discussed here in an earlier post.

\ No newline at end of file diff --git a/blog/zola-review/index.html b/blog/zola-review/index.html new file mode 100644 index 0000000..9a3f849 --- /dev/null +++ b/blog/zola-review/index.html @@ -0,0 +1 @@ +Review of Zola

Review of Zola

2023-11-05

As revealed in the previous post this site is now migrated into the Zola SSG management system. Zola is based on rust and boasts performance. What really caught my eye though is the numerous readymade light-weight themes available for it. Which is a refreshing change from the heavy theme this site used previously in the Jekyll framework. With its simplicity and performance, Zola has gained recognition among developers and content creators as a powerful tool for creating websites with minimal effort.

Its key features are speed and efficiency; flexible theming; built-in shortcodes; taxonomies and sections.

Some of it's other pros are:

  1. Rust Powered: The use of Rust makes Zola incredibly fast and robust, ensuring that your site will be blazingly quick and reliable.

  2. Simple Setup: Zola is easy to set up and use, making it a great choice for both newcomers to static site generation and experienced developers.

  3. Great Documentation: Zola provides extensive documentation that covers all aspects of the tool, making it accessible and user-friendly.

  4. Active Development: The Zola project is actively maintained, with regular updates and improvements, ensuring it stays up to date with modern web development trends.

  5. Community and Support: Zola has an active community and forum, offering support and assistance to users.

Cons:

  1. Limited theme options: Zola has a limited number of pre-built themes available. On the other hand most of the options here are lightweight (which is my preference).

  2. Learning Curve: Zola is known to have a steeper learning curve, but in my experience it is on par with other SSGs such as Jekyll.

Zola is an impressive static site generator that combines the power of Rust with user-friendliness, resulting in a tool that's efficient and flexible. Its speed, extensibility, and built-in features make it an excellent choice for developers and content creators looking to build modern, performant static websites. While it may not have the largest ecosystem, Zola's strengths more than make up for it.

Many people now prefer Zola for thier SSG blog; and it's a current trend to migrate to it.. Read more about Zola on their website: https://www.getzola.org/. Various deployment methods are discussed on https://www.getzola.org/documentation/deployment/overview/.

\ No newline at end of file diff --git a/blog/zola-switch/index.html b/blog/zola-switch/index.html new file mode 100644 index 0000000..75ab398 --- /dev/null +++ b/blog/zola-switch/index.html @@ -0,0 +1 @@ +Switched site to Zola SSG

Switched site to Zola SSG

2023-11-03

This blog site has now switched to the Zola static site generator framework. Previously it was based on Jekyll (the default github pages framework). Zola is based on Rust and is more lightweight. The look of the new site is also more lightweight.

This article will also serve as a trigger for testing on-push deployment and test the auto site generation!

In other news I'm looking to learning a web development framework. Specifically the LAMP stack... I'll log any update here if notable.

\ No newline at end of file diff --git a/categories/asm/index.html b/categories/asm/index.html new file mode 100644 index 0000000..e69de29 diff --git a/categories/bl702/index.html b/categories/bl702/index.html new file mode 100644 index 0000000..e69de29 diff --git a/categories/index.html b/categories/index.html new file mode 100644 index 0000000..e69de29 diff --git a/categories/misc/index.html b/categories/misc/index.html new file mode 100644 index 0000000..e69de29 diff --git a/categories/nrf/index.html b/categories/nrf/index.html new file mode 100644 index 0000000..e69de29 diff --git a/categories/nuttx/index.html b/categories/nuttx/index.html new file mode 100644 index 0000000..e69de29 diff --git a/categories/riotos/index.html b/categories/riotos/index.html new file mode 100644 index 0000000..e69de29 diff --git a/categories/update/index.html b/categories/update/index.html new file mode 100644 index 0000000..e69de29 diff --git a/feed.xml b/feed.xml new file mode 100644 index 0000000..498e566 --- /dev/null +++ b/feed.xml @@ -0,0 +1,3284 @@ + + + Ajit Ananthadevan + Interested in everything Embedded. + + + 2023-11-14T00:27:00+00:00 + https://ntn888.github.io/feed.xml + + Zephyr-7b-beta: Or how to run a ChatGPT alternative on an 8GB Graphics Card + 2023-11-14T00:27:00+00:00 + 2023-11-14T00:27:00+00:00 + + https://ntn888.github.io/blog/llama-howto/ + <p>Recently we have seen the rise of the small LLMs. As discussed in <a href="https://ntn888.github.io/blog/llama2/">this article</a> the <a rel="nofollow noreferrer" href="https://huggingface.co/TheBloke/zephyr-7B-beta-GGUF">Zephyr-7b-beta</a> model is making ground for it's impressive results. Here we shall see how to run the model locally on your system, all you need is a GPU with 8GB VRAM. My <a href="https://ntn888.github.io/blog/x99-motherboards/">workstation</a> sports an AMD RX 580 card that is ideally suited for this task! While this guide is targetted towards AMD cards note that you can run it on NVIDIA as well. On my system it runs at speed around 7-9 tokens/sec.</p> +<p>We will use the 'Text generation web UI' <a rel="nofollow noreferrer" href="https://github.com/oobabooga/text-generation-webui">https://github.com/oobabooga/text-generation-webui</a> as our client. This is the software that helps us to host and use the LLM model.</p> +<p>First we need to install what is called ROCm. This is a library that works on top of the AMD GPU driver. The following steps assume you're running POP-OS 22.04. You can follow this <a rel="nofollow noreferrer" href="https://github.com/danrauch/HOWTO-PopOS-AMD-HIP-with-Blender">guide</a>. We need to install version 5.6 (as of Nov 2023). The requirement is dictated by 'Text generation web UI', so see its documentation for the exact version you need in future.</p> +<blockquote> +<p>You may wish to install <code>radeontop</code>, an app that'll give various deets of the running status of the GPU. And run it alongside in another terminal while you prompt away.</p> +</blockquote> +<p>Once ROCm is installed we can proceed with installing 'Text generation web UI'.</p> +<p>First we need to set an environment variable:</p> +<pre class="z-code"><code><span class="z-text z-plain">export ROCBLAS_TENSILE_LIBPATH=/opt/rocm/lib/rocblas/library/ +</span></code></pre> +<blockquote> +<p>You'll have to run the above evertime before you invoke the 'Text generation web UI' with <code>./start_linux.sh</code> or the app will crash when you load in your model.</p> +</blockquote> +<p>Finally:</p> +<pre class="z-code"><code><span class="z-text z-plain">git clone https://github.com/oobabooga/text-generation-webui.git +cd text-generation-webui +./start_linux.sh +</span></code></pre> +<p>That last command is a convenient script that on first run does the actual installation. It will create the directory <code>installer_files</code>. When prompted for the type of installation enter <code>B</code> for AMD GPU.</p> +<p>It'll download a couple of GBs and take some time.</p> +<p>Once finished open up the displayed link in your browser. You'll be greeted with the 'Text generation web UI' interface.</p> +<p>The settings will be easily overwhelming at first with lots of variables but I'm going to present here what worked on my 8GB AMD card!</p> +<p>Obviously first we need to download a model. Go to the <code>Model</code> tab and enter <code>TheBloke/zephyr-7B-beta-GGUF</code> in the <code>Download Model</code> field. In the second <code>File name</code> box enter: <code>zephyr-7b-beta.Q4_K_M.gguf</code> which is recommended for 8GB cards. You can see details in the HuggingFace card <a rel="nofollow noreferrer" href="https://huggingface.co/TheBloke/zephyr-7B-beta-GGUF">page</a>.</p> +<p>Once it's downloaded push the reload button next to the model name <code>blank</code> on the top and then select the model name. Then set the following options below:</p> +<pre class="z-code"><code><span class="z-text z-plain">n-gpu-layers: anything above 35 +n_ctx: 8000 +</span></code></pre> +<p>Finally press load. This will take some time. Then goto <code>Parameters</code> tab. Under <code>Generation</code>:</p> +<pre class="z-code"><code><span class="z-text z-plain">max_new_tokens: 2000 +top_p: 0.95 +top_k: 40 +</span></code></pre> +<p>Under <code>Instruction Template</code>: Choose <code>ChatML</code>.</p> +<p>That's it for settings.</p> +<p>Now go to <code>Default</code> tab and paste the following prompt:</p> +<pre class="z-code"><code><span class="z-text z-plain">&lt;|system|&gt; +You are a creative writing assistant +&lt;|user|&gt; +&lt;your prompt here&gt; +&lt;|assistant|&gt; +</span></code></pre> +<p>Substitue it with your prompt and have fun!</p> +<p>Zephyr-7b brings the excitement of a capable model into the hands of affordable 8Gig GPU cards. Very quickly you'll be amazed at the results, although there is some telling difference from the present benchmark - GPT-4!</p> + + + + Checking out a free opensource ChatGPT alternative: Zephry-7b-Beta + 2023-11-12T22:27:00+00:00 + 2023-11-12T22:27:00+00:00 + + https://ntn888.github.io/blog/llama2/ + <p>In a previous <a href="https://ntn888.github.io/blog/llama/">article</a> we reviewed the free LLM Llama2, the 70b version. This is prohibitively large to run on your local machines as it requires around 48GB of GPU VRAM. I instead tried the 13b version but it was still too large for <a href="https://ntn888.github.io/blog/x99-motherboards/">my GPU</a> with only 8Gigs of VRAM. So ran it on my CPU instead and it was painfully slow.. Here we will look at a viable alternative: Zephry-7b-Beta, the latest sensation in the world of natural language processing!</p> +<p>Zephry-7b-Beta is a groundbreaking large language model that has been capturing the attention of the scientific community due to its exceptional abilities in natural language processing, despite being a relatively small model. This innovative language model, developed by the research wing of <a rel="nofollow noreferrer" href="https://huggingface.co/">HuggingFace</a>, boasts impressive feats that challenge the traditional notion that larger models are better. Although Zephry-7b-Beta has just over 7 billion parameters, in many benchmarks, outperforms larger models like GPT-3 and BERT-Large, which have more than 175 billion parameters each. This remarkable performance has been attributed to the model's unique architecture and training methods, which have allowed it to achieve a balance between accuracy and efficiency, making it an ideal candidate for real-world applications where resource constraints can pose a challenge. As in the case of hosting on a home PC.</p> +<p>It gives out an impressive interactive experience. And I found that it resembled GPT4 more than the Llama 2 model.</p> +<p>I first sought to install it locally as this is reported to run comfortably on 8G GPUs. So I downloaded the prerequisite library ROCm (for driving the AMD GPU), and then installed <a rel="nofollow noreferrer" href="https://github.com/oobabooga/text-generation-webui"><code>Text generation web UI</code></a>. Downloaded the LLM model from <a rel="nofollow noreferrer" href="https://huggingface.co/TheBloke/zephyr-7B-beta-GGUF">HuggingFace site</a>. Some of the troubleshooting steps I took can be seen in this <a rel="nofollow noreferrer" href="https://github.com/oobabooga/text-generation-webui/issues/4558">thread</a>. But again it was too slow to be usable.. Maybe I'm doing something wrong.</p> +<p>Then I resorted to hosting on a cloud GPU provider. I made an account at <a rel="nofollow noreferrer" href="https://vast.ai/">vast.ai</a> (these folks being the cheapest). I made an RTX A6000 (48GB VRAM) instance with 8G disk space. Downloaded the same model image (the Q5_K_M variant). Then I made parameter choices in <code>Text generation web UI</code> (not necessarily optimum, but sure something to play around with and test):</p> +<ul> +<li>Set Generation/Preset to debug-deterministic</li> +<li>Increase max new tokens to 2000</li> +<li>ChatML for Instruction template</li> +</ul> +<p>Also remember to set <code>n-gpu-layers</code> to over 35 for this gguf model when loading it.</p> +<blockquote> +<p>There are plenty of Youtube videos on how to use <code>Text generation web UI</code>.</p> +</blockquote> +<p>And used the following prompt (copied from the model page and tweaked):</p> +<pre class="z-code"><code><span class="z-text z-plain">&lt;|system|&gt; +You are a creative writing assistant +&lt;|user|&gt; +&lt;your prompt here&gt; +&lt;|assistant|&gt; +</span></code></pre> +<p>I have to say.. The results were snappy. And like I said earlier the responses were impressive.</p> +<p>Although I have been using the Default/Notebook input method of the <code>Text generation web UI</code>, I'm looking to study the chat prompt. And see if it gives a more fluid interactive experience.</p> +<p>This has been my log of trying out a free new ChatGPT alternative. Although I didn't know why it didn't run well on my local system since this is most suited for this task. Let me know what your goto model is these days!</p> + + + + Introduction to AliExpress for the DIY Enthusiast + 2023-11-10T10:27:00+00:00 + 2023-11-10T10:27:00+00:00 + + https://ntn888.github.io/blog/aliexpress-intro/ + <p>Hey there, DIY enthusiasts and crafty creators! If you’ve got a knack for tinkering and love a good deal, let me introduce you to your next favorite haunt: <a rel="nofollow noreferrer" href="https://www.aliexpress.com/">AliExpress</a>. Items are sourced directly from Chinese sellers, and for us Makers this means they originate from Shenzen more often than not. Another contrast with ebay is that all the listings are <em>buy it now</em> only.</p> +<p><strong>What's So Great About AliExpress for DIYers?</strong></p> +<p>AliExpress is like an infinite toolbox for the DIY enthusiast. It’s crammed with components, from resistors to robotics, at prices that won’t have you eating ramen for a month (unless that’s your project). Need a tiny screw for your drone? A vintage-looking LED for that retro radio? AliExpress has it all, and then some.</p> +<p><strong>Navigating AliExpress</strong></p> +<p>AliExpress's interface is pretty straightforward. Start with a search for your desired component or tool, and then dive into the rabbit hole of options. Use filters to narrow down to the specifics like price range, free shipping, and more.</p> +<p><strong>Tips and Precautions for the Savvy Shopper</strong></p> +<ol> +<li> +<p><strong>Read Reviews and Ratings</strong>: These are your compass in the vast sea of products. They can guide you to quality finds and steer you clear of duds.</p> +</li> +<li> +<p><strong>Check Seller Credentials</strong>: Look for sellers with high ratings and a long history of transactions. They’re generally the ones who have proven their reliability.</p> +</li> +<li> +<p><strong>Beware of Too-Good-To-Be-True Deals</strong>: If it looks like a steal, it might just be one—literally. So if you find a Raspberry Pi for the price of a real pie, think twice.</p> +</li> +<li> +<p><strong>Understand the Specs</strong>: Make sure you understand the specifications of what you're buying. That servo motor might look like it fits your needs, but double-check the torque and speed ratings.</p> +</li> +</ol> +<p><strong>The Long Wait: Shipping Timelines on AliExpress</strong></p> +<p>Here’s the kicker: patience is not just a virtue on AliExpress; it’s a necessity. Shipping can take anywhere from a few weeks to a couple of months. So, if you're planning to build that LED cube for a party next week, you might want to look locally instead.</p> +<p><strong>Conclusion: Is AliExpress Worth It for DIYers?</strong></p> +<p>In a nutshell, absolutely! AliExpress is a DIY haven. Yes, you’ll wait longer for shipping, and yes, you need to do your homework on what you’re buying. But the variety is unbeatable, the prices are right, and the sense of community in the review sections can be genuinely heartwarming. So, go forth and create – AliExpress has your back.</p> +<p>So, get those creative gears turning and start browsing! With a bit of savvy shopping and some strategic planning, AliExpress can be a goldmine for the DIY electronics hobbyist. Happy inventing!</p> + + + + Mastering the Llama: Unleashing the Power of Self-Hosted AI with Llama GPT + 2023-11-10T03:27:00+00:00 + 2023-11-10T03:27:00+00:00 + + https://ntn888.github.io/blog/llama/ + <p>In the vibrant realm of artificial intelligence and machine learning, the democratization of technology has been a game-changer. The latest buzz in the tech community is about <a rel="nofollow noreferrer" href="https://github.com/getumbrel/llama-gpt">Llama GPT</a>, a free, open-source alternative to ChatGPT that enthusiasts can self-host. For those of us who relish the control and customization that comes with running software on our own machines, Llama GPT is like a beacon in the AI space.</p> +<p>I recently decided to bring Llama GPT into my personal fold. There’s something incredibly satisfying about hosting such powerful technology locally. Llama GPT offers a range of model sizes to cater to different needs and capacities, and I opted for the 13B version, with a model size of roughly 7GB.</p> +<p>The prerequisite for running Llama GPT on Linux is Docker, which aligns perfectly with the setup on my Intel Xeon E5 2670v3-powered workstation. As I’ve shared in a previous <a href="https://ntn888.github.io/blog/x99-motherboards/">blog post</a>, my system sports 12 cores and 24 threads, a configuration that one would presume quite capable of handling a large language model.</p> +<p><img src="/img/llama-gpt-pc.resized.png" alt="Llama logo on PC" /></p> +<p>However, my initial enthusiasm met with a challenge. The response times were sluggish, averaging around a minute per response. This was a stark contrast to the snappy interactions I've had with the online ChatGPT.</p> +<p>Quality-wise, the responses from the 13B model of Llama GPT didn't quite measure up to the standards set by GPT-4, which wasn't entirely surprising. The 13B model is a mid-tier offering, and I hadn't sprung for the massive 70B model, which requires a daunting 41GB of system RAM.</p> +<p>In search of a benchmark, I stumbled upon a free <a rel="nofollow noreferrer" href="https://stablediffusion.fr/llama2">online version</a> of the 70B model. However, this service appeared to have a cap on the number of words per interaction, which made a head-to-head comparison with GPT-4 challenging.</p> +<p>This experience got me thinking about the trade-offs between self-hosted and cloud-based AI models. Self-hosting offers several advantages, like data privacy, no API costs, and the freedom to tweak the system to your liking. But it also comes with its own set of challenges, such as the need for significant computational resources and the potential for slower response times, as I learned firsthand.</p> +<p>To give you a better idea, running a 13B model like Llama GPT is no small feat. It requires not only a powerful processor but also a substantial amount of RAM. While my Xeon processor is no slouch, AI model inference speed depends heavily on parallel processing capabilities, something that's inherently optimized in cloud-based solutions like ChatGPT, which run on specialized hardware designed for high-performance computing tasks.</p> +<p>Furthermore, the sheer size of these models and the resources they require to run smoothly means that self-hosting is often not as cost-effective or energy-efficient as using a cloud service. Cloud providers can leverage economies of scale, deploying thousands of optimized processors to serve millions of users, which individual self-hosters simply can't match.</p> +<p>Despite these hurdles, the allure of a self-hosted AI model remains. For hobbyists and tinkerers, it's an exciting prospect to have your own AI that you can interrogate and interact with on your terms, without sending data across the internet. It's a sandbox for experimentation and learning that aligns with the ethos of DIY and hands-on discovery.</p> +<p>But it's essential to manage expectations. As of now, self-hosted AI models like Llama GPT serve more as educational tools and proofs of concept rather than competitors to established cloud-based services. They offer a glimpse into the inner workings of language models and allow enthusiasts to experiment with AI on their terms.</p> +<p>To wrap up, while my experiments with Llama GPT may not have delivered the speed and quality of ChatGPT, they've provided invaluable insights into the capabilities and limitations of self-hosting large-scale AI models. For those in the embedded development and IoT community, it's a reminder of the exciting possibilities and the practical considerations that come with the territory of cutting-edge tech. It’s a balancing act between the thrill of self-hosting and the performance of cloud-based services—a decision each of us in the community will weigh based on our individual needs and curiosity.</p> + + + + First project in RIOT-OS: OLED + 2023-11-10T01:27:00+00:00 + 2023-11-10T01:27:00+00:00 + + https://ntn888.github.io/blog/riot-oled/ + <p>Today, we're diving into a nifty project that's as exciting as it is educational. We're going to use the STM32F103C8 &quot;Blue Pill&quot; microcontroller and Riot-OS to drive an SSD1306 I2C OLED module – and trust me, it's going to be a blast!</p> +<p>First off, for those new to the playground, let's give a quick intro to our main characters. The STM32F103C8, affectionately known as the &quot;Blue Pill,&quot; is a seriously cost-effective microcontroller that packs a punch with an ARM Cortex-M3 processor. Then there's Riot-OS, an open-source operating system that's optimized for IoT devices. It's like the life of the party for microcontrollers, giving them the software needed to interact with the world. And the SSD1306 is a tiny OLED display that’s so easy to get along with; it communicates over I2C, which means we can have a nice little chat with just a couple of wires.</p> +<p>So, let's get to the meat and potatoes.</p> +<p><strong>Materials Needed:</strong></p> +<ul> +<li>STM32F103C8 &quot;Blue Pill&quot; microcontroller</li> +<li>SSD1306 I2C OLED display</li> +<li>Connecting wires</li> +<li>USB to Serial converter for programming</li> +</ul> +<p><strong>Step 1: Setting Up the Environment</strong> +Before we start, ensure you have a programming environment set up for the STM32F103C8. You'll need the ARM GCC compiler and OpenOCD for flashing. For Riot-OS, clone the repository from GitHub and get the build environment ready.</p> +<p><strong>Step 2: Wiring Up</strong> +Connect your SSD1306 display to the Blue Pill. The display needs four connections:</p> +<ul> +<li>VCC to 3.3V</li> +<li>GND to GND</li> +<li>SCL to B6 (or whichever I2C clock pin you prefer)</li> +<li>SDA to B7 (or your chosen I2C data pin)</li> +</ul> +<p><strong>Step 3: Bootstrapping Riot-OS</strong> +Navigate to the Riot-OS base directory and create a new project for our Blue Pill. You can find a board configuration for the Blue Pill in Riot's boards directory if you don't feel like starting from scratch.</p> +<p><strong>Step 4: Writing The Code</strong> +Here's a simple skeleton to start with in your <code>main.c</code>:</p> +<pre data-lang="c" class="language-c z-code"><code class="language-c" data-lang="c"><span class="z-source z-c"><span class="z-meta z-preprocessor z-include z-c"><span class="z-keyword z-control z-import z-include z-c">#include</span> <span class="z-string z-quoted z-other z-lt-gt z-include z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&lt;</span>stdio.h<span class="z-punctuation z-definition z-string z-end z-c">&gt;</span></span> +</span><span class="z-meta z-preprocessor z-include z-c"><span class="z-keyword z-control z-import z-include z-c">#include</span> <span class="z-string z-quoted z-double z-include z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>xtimer.h<span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span> +</span><span class="z-meta z-preprocessor z-include z-c"><span class="z-keyword z-control z-import z-include z-c">#include</span> <span class="z-string z-quoted z-double z-include z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>periph_conf.h<span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span> +</span><span class="z-meta z-preprocessor z-include z-c"><span class="z-keyword z-control z-import z-include z-c">#include</span> <span class="z-string z-quoted z-double z-include z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>periph/i2c.h<span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span> +</span><span class="z-meta z-preprocessor z-include z-c"><span class="z-keyword z-control z-import z-include z-c">#include</span> <span class="z-string z-quoted z-double z-include z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>ssd1306.h<span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span> +</span><span class="z-meta z-preprocessor z-include z-c"><span class="z-keyword z-control z-import z-include z-c">#include</span> <span class="z-string z-quoted z-double z-include z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>ssd1306_params.h<span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span> +</span> +<span class="z-storage z-type z-c">int</span> <span class="z-meta z-function z-c"><span class="z-entity z-name z-function z-c">main</span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-storage z-type z-c">void</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-meta z-function z-c"> +</span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span></span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"> + <span class="z-meta z-function-call z-c"><span class="z-support z-function z-C99 z-c">puts</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>STM32 + SSD1306 OLED screen with RIOT<span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-comment z-block z-c"><span class="z-punctuation z-definition z-comment z-c">/*</span> Initialize the OLED display <span class="z-punctuation z-definition z-comment z-c">*/</span></span> + ssd1306_t dev<span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span><span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">ssd1306_init</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-keyword z-operator z-c">&amp;</span>dev<span class="z-punctuation z-separator z-c">,</span> <span class="z-keyword z-operator z-c">&amp;</span>ssd1306_params<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span> <span class="z-keyword z-operator z-comparison z-c">!=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-support z-function z-C99 z-c">puts</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>Initialization failed<span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-flow z-return z-c">return</span> <span class="z-constant z-numeric z-integer z-decimal z-c">1</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">ssd1306_set_contrast</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-keyword z-operator z-c">&amp;</span>dev<span class="z-punctuation z-separator z-c">,</span> <span class="z-constant z-numeric z-integer z-hexadecimal z-c"><span class="z-punctuation z-definition z-numeric z-base z-c">0x</span>FF</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> <span class="z-comment z-line z-double-slash z-c"><span class="z-punctuation z-definition z-comment z-c">//</span> Max contrast +</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">ssd1306_display_on</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-keyword z-operator z-c">&amp;</span>dev<span class="z-punctuation z-separator z-c">,</span> <span class="z-constant z-language z-c">true</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-comment z-block z-c"><span class="z-punctuation z-definition z-comment z-c">/*</span> Display some text on the screen <span class="z-punctuation z-definition z-comment z-c">*/</span></span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">ssd1306_write_string</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-keyword z-operator z-c">&amp;</span>dev<span class="z-punctuation z-separator z-c">,</span> <span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>Hello, IoT World!<span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-keyword z-control z-c">while</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span><span class="z-constant z-numeric z-integer z-decimal z-c">1</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-comment z-block z-c"><span class="z-punctuation z-definition z-comment z-c">/*</span> You could add some code here to update the display <span class="z-punctuation z-definition z-comment z-c">*/</span></span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">xtimer_sleep</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-constant z-numeric z-integer z-decimal z-c">1</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + + <span class="z-keyword z-control z-flow z-return z-c">return</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> +</span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-end z-c">}</span></span></span> +</span></code></pre> +<p><strong>Step 5: Building and Flashing</strong> +To compile and flash your code to the Blue Pill, use the following commands from your project's directory:</p> +<pre data-lang="shell" class="language-shell z-code"><code class="language-shell" data-lang="shell"><span class="z-text z-plain">make BOARD=bluepill flash +</span></code></pre> +<p>Assuming all goes well, you should see your &quot;Hello, IoT World!&quot; message on the display.</p> +<p><img src="/img/blue-pill_riot_oled.resized.png" alt="Printout OLED result" /></p> +<p><strong>Step 6: Experimenting</strong> +Now that you've got the basics down, why not play around with the display? Riot-OS has a rich set of libraries for graphics, so you can draw shapes, display sensor data, or even create a mini game on your OLED.</p> +<p><strong>Step 7: Debugging</strong> +If you run into any issues, remember: debugging is half the fun in embedded systems. Check your connections, ensure the I2C addresses match, and use the <code>make term</code> command to peek into the logs.</p> +<p><strong>Wrapping Up</strong> +You've just made a microcontroller communicate with an OLED display, and that's pretty darn cool. The Blue Pill and Riot-OS are like peanut butter and jelly – they just work beautifully together.</p> +<p>Remember, the key to mastery is experimentation. So, tweak the contrast, flip the display, scroll text, and see what your Blue Pill can do. With the SSD1306, your Blue Pill isn’t just crunching numbers; it’s showing off its results with style.</p> +<p>And there you have it, folks – a simple, yet thrilling journey through embedded development with a dash of IoT. Keep tinkering, keep learning, and most importantly, keep having fun. Until next time, may your connections be stable and your code bug-free!</p> + + + + Building an affordable electronics lab for hobbyists + 2023-11-09T20:27:00+00:00 + 2023-11-09T20:27:00+00:00 + + https://ntn888.github.io/blog/building-lab/ + <p>Hey fellow tinkerers and circuit wizards!</p> +<p>In this post, we're diving into one of the most exhilarating journeys you can embark on from the comfort of your own home — building your very own electronics lab! And guess what? You don't need a treasure chest to set it up. With a few savvy picks from AliExpress.com, you can get the essentials without breaking the bank. Let's get charged up and start this electrifying adventure!</p> +<p><img src="/img/electronics_lab.jpg" alt="example project image" /></p> +<p><strong>Laying the Foundation with an Electronics Starter Kit</strong></p> +<p>First up, the heart of your lab: an electronics starter kit. This isn't just a box of components; it's the seed from which countless projects will grow. AliExpress has a plethora of options, but look for a kit that's brimming with resistors, capacitors, LEDs, and transistors. Don't skimp on variety here — the more components, the merrier. Make sure it includes a breadboard, jumper wires, and maybe even a small project book to spark your creativity.</p> +<p><strong>The Multimeter: Swiss Army Knife of Embedded Hobbyists</strong></p> +<p>We evaluated various options in <a href="https://ntn888.github.io/blog/multimeters/">this post</a>.</p> +<p><strong>The Power to Innovate: Basic Lab Power Supply</strong></p> +<p>Next, we need a trusty sidekick: a basic lab power supply. You don't need a high-end model for starter projects. A simple adjustable power supply with a range of 0-30 volts and 0-5 amps should suffice. This will let you power almost any project you dream up. Some supplies come with nifty features like current limiting, short circuit protection, and digital displays — these are nice-to-have features that can prevent your projects from going up in smoke.</p> +<p><strong>Crucial Beats: The Cheap Signal Generator</strong></p> +<p>Now, let's talk about setting the tempo with a signal generator. For beginners and budget-conscious builders, a simple function generator from AliExpress will do the trick. Look for one that can dish out square, sine, and triangular waves. Frequency range is key; make sure it can go up to at least 1MHz. This little gizmo is like the metronome for your circuits, essential for testing filters, amplifiers, and so much more.</p> +<p><strong>Microcontrollers: The Brains of the Operation</strong></p> +<p>Microcontrollers are where things get really juicy. They're the brains of your lab, the tiny maestros conducting your electronic symphonies. You don't need the latest and greatest; a few noteworthy contenders can be had for a song. The classics like Arduino Uno or Nano are always a good start (if you go the Arduino route)— user-friendly for beginners and versatile enough for advanced projects. But in this blog we aren't inclined towards Arduino.. See <a href="https://ntn888.github.io/blog/mcu-selection-guide/">this post</a> for further breakdown.</p> +<p><strong>Storing Your Electronic Treasures</strong></p> +<p>Organization is key to a functioning lab. You can snag component organizers from AliExpress for less than the cost of a fancy coffee. Keep those resistors, capacitors, and tiny ICs in check, and you'll save yourself from the headache of hunting for a 10k resistor in a mountain of electronic confetti.</p> +<p><strong>Soldering: The Alchemy of Electronics</strong></p> +<p>No lab is complete without a decent soldering iron. You don't need a station that could solder a satellite; a basic adjustable temperature iron will serve you well. And a tip on tips: always use a chisel tip; no matter how small the component. Fine tips are not useful since they don't act as a good heat reservoir. For SOIC/SMD soldering lookup on <em>drag soldering</em>. And dont forget the flux.</p> +<p><strong>Illumination: Because You Can't Solder What You Can't See</strong></p> +<p>Don't forget about lighting! A well-lit workspace is crucial. AliExpress offers a variety of LED desk lamps that won't cast a shadow over your budget. Consider one with a magnifying glass; it's like having an eagle-eyed assistant.</p> +<p><strong>Learning Resources: Knowledge is Power</strong></p> +<p>Lastly, the internet is an ocean of resources, but a couple of well-chosen books can be your anchors. Get on Amazon, look for beginner's guides or project books that resonate with your interests, whether it's robotics, home automation, or something else that gets your electrons excited. A few free copy-left books to learn circuit theory was discussed <a href="https://ntn888.github.io/blog/analog-filters-primer/">here</a>.</p> +<p><strong>Safety First!</strong></p> +<p>Before I sign off, a word to the wise: safety is paramount. Always double-check connections before powering up, never work on a live circuit, and invest in a pair of safety goggles. Your future self will thank you.</p> +<p>Building an electronics lab is like piecing together your very own technical wonderland. It's a place where imagination meets reality, where learning meets doing, and where the only limit is the scope of your curiosity. And the best part? It doesn't have to cost an arm and a leg. With these tips and AliExpress as your marketplace, you're all set to wire up your world.</p> +<p>Stay charged, my friends, and happy building!</p> + + + + Lutris: The Linux Gaming Hero + 2023-11-09T17:27:00+00:00 + 2023-11-09T17:27:00+00:00 + + https://ntn888.github.io/blog/linux-gaming/ + <p>Hey there, fellow tech enthusiasts! Today, I'm taking a little detour from our usual DIY projects to dive into the exciting world of Linux gaming. Yeah, you heard me right – gaming on Linux is a thing, and it's not just for tech wizards in their secret lairs. Diving into the realm of Linux gaming, we often find ourselves tangled in a web of emulators, Wine dependencies, and a barrage of terminal commands that can intimidate even the most ardent of enthusiasts. Enter <a rel="nofollow noreferrer" href="https://lutris.net/">Lutris</a>, the unsung hero of Linux gaming that has been quietly revolutionizing our gaming experiences over the past few years.</p> +<p><img src="/img/lutis_gaming.resized.png" alt="Tux gaming" /></p> +<p>Now, if you're like me, you might have witnessed the evolution of tooling and development environments in the embedded world. Well, the same winds of change have been blowing in the gaming sphere, especially for our Linux buddies. In the last decade, the progress has been nothing short of remarkable.</p> +<p>Gone are the days when Linux was just a powerhouse for servers and a playground for developers. It's the dawning of a new era, where Linux doesn't just mean business; it's serious about play, too. Enter Lutris, a game changer (pun intended!) that's been turbocharging the Linux gaming scene in recent years.</p> +<p>Lutris, in its essence, is an optimizer's dream. It transforms the often labyrinthine task of game management into a streamlined, unified process. With Lutris, you're not just running games; you're curating an entire library with a tool that's as versatile as a Swiss Army knife.</p> +<p>The beauty of Lutris lies in its simplicity. Installation scripts, courtesy of the community's collective wisdom, are your roadmap to hassle-free gaming. These scripts are like the secret sauce that makes a good dish great—they handle the complexities of configuration so you can focus on what matters: the game itself.</p> +<p>So, what's the deal with Lutris? Well, it's a game manager, sure, but it's also a treasure trove for tweaking and optimizing your gaming experience. It supports a plethora of games, including those Windows titles that never thought they'd see the light of a Linux desktop. With Lutris, you can manage game installations, customize settings, and even sync your game library across different platforms.</p> +<p>The underlying engine - <a rel="nofollow noreferrer" href="https://www.protondb.com/">Proton</a> stands as a crucial element in the realm of Linux gaming compatibility, developed by Valve Corporation. It functions as a compatibility layer, enabling the seamless execution of Windows games on the Linux operating system. Leveraging the power of Vulkan and other technologies, Proton strives to overcome the historical barrier between Windows-exclusive titles and the Linux platform. Under constant development, it incorporates optimizations and improvements to enhance compatibility with a wide array of games. With its integration into the Steam client, Proton has significantly expanded the gaming library accessible to Linux users, contributing to the ongoing efforts to establish Linux as a viable gaming platform. Its serious approach to delivering compatibility aligns with the industry's shift towards broader inclusivity and accessibility in gaming.</p> +<p>From a technical standpoint, Lutris leverages compatibility layers like Wine and Proton to bridge the gap between Linux and Windows game libraries. It's like having a translator that enables games written for one platform to speak the language of another. This technical wizardry is crucial for expanding the gaming catalog on Linux.</p> +<p>Now, let's address the elephant in the room – running pirated games on Linux. In my experience Lutris does a better work of running these than the native Windows OS! Lutris has a handy <em>Install from EXE</em> option that's handy just for this case. You just have to mount the download (if required) and point Lutris to the Setup executable. Easy! I use a separately partitioned instance of PoPOS and have lutris installed on it for my gaming needs. This is a way to declutter from my main PoPOS install for my dev environment needs. As installing games tend to be time intensive, I can leave the gaming partition alone when I upgrade/reinstall the workstation one.</p> +<p>There you go! An overview of Lutris and its significant contribution to elevating the gaming experience on Linux. It’s a testament to what the open-source community can achieve, and a beacon for gamers looking to Linux as a viable gaming platform.</p> + + + + Unveiling the Swiss Army Knife of Embedded Hobbyists: The Multimeter + 2023-11-09T14:27:00+00:00 + 2023-11-09T14:27:00+00:00 + + https://ntn888.github.io/blog/multimeters/ + <p>Embarking on the thrilling journey of embedded development is akin to diving into a sea of components, wires, and circuits. Amidst this electronic labyrinth, there exists a humble yet indispensable tool—the multimeter. Today, let's unravel the mysteries of this electronic wizardry and explore why every embedded hobbyist should have a trusty multimeter by their side.</p> +<p><img src="/img/multimeter_using.jpeg" alt="Using a multimeter" /></p> +<ol> +<li> +<p><strong>Versatility Personified:</strong></p> +<ul> +<li><strong>Voltage Measurements:</strong> From checking power supplies to ensuring proper voltage levels, a multimeter is your go-to tool for accurate voltage measurements.</li> +<li><strong>Current Readings:</strong> Understand the flow of current through your circuit with ease, helping you troubleshoot and optimize power consumption.</li> +</ul> +</li> +<li> +<p><strong>Resistance to the Rescue:</strong></p> +<ul> +<li><strong>Continuity Testing:</strong> Track down open circuits and ensure seamless connectivity by employing the multimeter's continuity testing feature.</li> +<li><strong>Resistance Measurements:</strong> Measure resistances in your components, aiding in identifying faulty elements and optimizing circuit design.</li> +</ul> +</li> +<li> +<p><strong>Diagnosing Component Health:</strong></p> +<ul> +<li><strong>Testing Capacitors:</strong> Verify the health of capacitors by measuring capacitance, helping you identify and replace aging or faulty components.</li> +<li><strong>Inductance Measurements:</strong> For those delving into the world of inductors, the multimeter assists in gauging inductance levels and diagnosing issues.</li> +</ul> +</li> +<li> +<p><strong>Precision in Troubleshooting:</strong></p> +<ul> +<li><strong>Fault Detection:</strong> Pinpoint faults and discrepancies in your circuitry, saving valuable time in the debugging process.</li> +<li><strong>Temperature Measurements:</strong> Some advanced multimeters even offer temperature measurements, adding another layer of diagnostic capability.</li> +</ul> +</li> +<li> +<p><strong>Safety First:</strong></p> +<ul> +<li><strong>Non-Invasive Testing:</strong> Conduct non-invasive tests without interrupting your circuit, ensuring the safety of both your components and yourself.</li> +<li><strong>Fuse Checks:</strong> Before powering up a circuit, use the multimeter to check fuses for continuity, preventing potential damage to your setup.</li> +</ul> +</li> +<li> +<p><strong>Essential for Prototyping:</strong></p> +<ul> +<li><strong>Prototyping Validation:</strong> During the prototyping phase, a multimeter aids in validating your circuit design and ensuring it aligns with your expectations.</li> +<li><strong>Real-Time Monitoring:</strong> Keep a close eye on voltage levels and other parameters in real-time, allowing for on-the-fly adjustments and improvements.</li> +</ul> +</li> +<li> +<p><strong>Cost-Effective Investment:</strong></p> +<ul> +<li><strong>Budget-Friendly:</strong> Multimeters come in a range of prices, making them an affordable yet invaluable investment for any embedded hobbyist.</li> +<li><strong>Longevity:</strong> With proper care, a good-quality multimeter can last for years, offering a long-term solution for your electronic endeavors.</li> +</ul> +</li> +</ol> +<h2 id="popular-examples">Popular Examples:<a class="zola-anchor" href="#popular-examples" aria-label="Anchor link for: popular-examples">#</a></h2> +<ol> +<li> +<p><strong>Neoteck Pocket Digital Multimeter:</strong></p> +<ul> +<li><em>Compact and Affordable:</em> The Neoteck Pocket Multimeter packs essential features into a budget-friendly package, making it ideal for beginners and hobbyists looking for a reliable yet economical option.</li> +</ul> +</li> +<li> +<p><strong><a rel="nofollow noreferrer" href="https://vi.aliexpress.com/item/1005005781615711.html">Aneng DM850 Digital Multimeter</a>:</strong></p> +<ul> +<li><em>Budget-Friendly Excellence:</em> With accurate readings, essential functionalities, and a user-friendly design, it stands out as an excellent choice for hobbyists and beginners seeking reliability without breaking the bank.</li> +</ul> +</li> +<li> +<p><strong>Etekcity Digital Multimeter MSR-R500:</strong></p> +<ul> +<li><em>Budget-Friendly Precision:</em> With a focus on simplicity and accuracy, the Etekcity MSR-R500 is a cost-effective choice for those seeking basic functionalities without breaking the bank.</li> +</ul> +</li> +<li> +<p><strong>AstroAI Digital Multimeter TRMS 6000 Counts:</strong></p> +<ul> +<li><em>Versatility on a Budget:</em> The AstroAI TRMS Multimeter provides an impressive range of features at an economical price point, catering to hobbyists who need reliability without splurging.</li> +</ul> +</li> +<li> +<p><strong>Tekpower TP8268 AC/DC Auto/Manual Range Digital Multimeter:</strong></p> +<ul> +<li><em>Affordable Auto-Range Functionality:</em> The Tekpower TP8268 offers auto-range capabilities without sacrificing affordability, making it a practical choice for users who want convenience on a budget.</li> +</ul> +</li> +</ol> +<p>In the world of embedded hobbyism, where creativity meets thriftiness, the multimeter proves to be an invaluable asset without burning a hole in your wallet. As we've delved into the capabilities of these cost-effective examples, it's evident that you don't need to break the bank to access the essential functionalities for your electronic projects.</p> +<p>So, whether you're a seasoned pro or just starting your embedded journey, consider the multimeter your electronic sidekick. With its ability to unravel the intricacies of your circuits and provide real-time insights, it's not just a tool; it's the key to unlocking the full potential of your projects. Embrace the power of the multimeter, and let your embedded endeavors flourish. Happy tinkering!</p> + + + + Building a Frugal Gaming PC + 2023-11-08T11:27:00+00:00 + 2023-11-08T11:27:00+00:00 + + https://ntn888.github.io/blog/x99-motherboards/ + <p><strong>Motherboards: The Refurbished x99 AliExpress Special</strong></p> +<p>Here we will talk about a new obsession of mine - refurbished LGA 2011v3 socket motherboards; paired up with used Intel E5 1600/2700 series processor forms the basis for a cheap gaming system. These are sold in AliExpress as a [motherboard, CPU, RAM] combo for upwards of USD80 depending on the manufacturer.</p> +<p>There's good information about these boards on the Russian site: <a rel="nofollow noreferrer" href="https://xeon-e5450.ru/">https://xeon-e5450.ru/</a>. Just use <em>Translate Web Pages</em> plugin for your browser to auto-translate.</p> +<p>There's also the Miyconst <a rel="nofollow noreferrer" href="https://github.com/miyconst/Mi899/blob/master/src/Mi899/README.md">Youtube Channel</a> that provides good information.</p> +<p>When I purchased, I personally went for the QIYIDA AliExpress seller (the product had tons of good reviews). And received the <a rel="nofollow noreferrer" href="https://xeon-e5450.ru/socket-2011-3/x99h/">x99h v1.41 board</a>. According to Miyconst this is a poor board. Mainly due to having apparently only single DDR RAM channel.</p> +<p>I also noticed that the motherboard doesn't support <em>Suspend</em> functionality. In the <strong>AHCI</strong> options in BIOS, <em>Suspend</em> value is permanently set to <em>Disabled</em>.</p> +<p>The one <a rel="nofollow noreferrer" href="https://www.aliexpress.com/item/1005004519470412.html">I purchased</a> came with the following combo:</p> +<ul> +<li>x99h v1.41 board (QIYIDA)</li> +<li>Intel Xeon E5 2670v3 processor (used)</li> +<li>16G (2x 8G) RAM QIYIDA brand (refurbished)</li> +</ul> +<p>It was priced at $110 on Jan 2023.</p> +<p>From my personal experience, I'd advice to do thorough checks (using the sources provided above) and not just rely on AliExpress rankings/ratings. I regret my purchase as I cannot put my PC to sleep (a functionality I heavily rely on).</p> +<p>The whole ecosystem is possible because of the enormous market of the used Xeon chips. The 2670v3 sells on Ali for a grand total of $7! But I recommend buying as a combo for a piece of mind due to any compatiblity issues.</p> +<p>Last comes our most important component: A Graphics card. Ali has us covered in this regard as well! For around $80 you can get a Radeon RX580 8G! Unbeatable! I believe you can go with the AliExpress reviews on this one.</p> +<p>On the bright side though (going with the Xeon 2670v3 processor, which has 12 cores 24 threads), it's refreshing to see the <em>System Monitor</em> report the 24 threads available on the system!</p> +<p><img src="/img/xeon-monitor-24-threads.png" alt="System Monitor screenshot" /></p> + + + + nRF52840 board (Nice!Nano clones) + 2023-11-07T20:27:00+00:00 + 2023-11-07T20:27:00+00:00 + + https://ntn888.github.io/blog/nrf-clones/ + <p>These Chinese Nicenano V2 clones have started popping up on AliExpress. This is great news for us low-power-wireless nerds; As this widens our choice for ready made boards..</p> +<blockquote> +<p>Nordic hasn't actually been welcoming towards hobbyists and this has resulted in scarcity of ready-made newbie friendly boards for prototyping (as opposed to esp32 or the ST's bluepill). Which has been a real game stopper for home IOT projects. We've discussed two possible boards in <a href="https://ntn888.github.io/blog/micro-bit-breakout/">this post</a>. Here is a new viable option.</p> +</blockquote> +<p><a rel="nofollow noreferrer" href="https://aliexpress.com/item/1005006035267231.html">NRF52840 Development Board Supermini Compatible With Nice!Nano V2.0</a></p> +<p><img src="/img/nrf_clone_pic.resized.png" alt="Main board pics" /></p> +<p><img src="/img/nrf_clone_pinout.resized.png" alt="Board pinouts" /></p> +<p>Features:</p> +<ul> +<li> +<p>Mini-sized (breadboard friendly)</p> +</li> +<li> +<p>Built-in LiPo battery charger</p> +</li> +<li> +<p>Supports charger boost jumper (100mA to 300mA) on the back side</p> +</li> +</ul> +<p>Unfortunately the SWD debug pins are not broken out as headers, but as pads underneath. We'd have to directly solder some wires onto this. I haven't checked them out personally, but they may be shipping with a bootloader for simply flashing purposes.</p> +<p>There is a known issue with higher leak current than the original micro board. This one is reported to have a deep sleep current consumption of 700mA! (<a rel="nofollow noreferrer" href="https://redd.it/16q5b2c">reddit</a>)</p> +<p>For more details and the workaround of the issue please follow the link to <a rel="nofollow noreferrer" href="https://github.com/joric/nrfmicro/wiki/Alternatives#supermini-nrf52840">this site</a>.</p> +<p>As the maker and IoT communities continue to grow, it's exciting to see how this clone will contribute to the world of embedded systems and innovation. Whether you're a seasoned developer or just starting your journey, the Nicenano V2 clone is worth exploring for your next project.</p> + + + + Best microcontroller for Beginners + 2023-11-05T18:27:00+00:00 + 2023-11-05T18:27:00+00:00 + + https://ntn888.github.io/blog/begin-embedded/ + <p>One of the most asked questions on r/embedded Reddit is &quot;How do I get started in embedded electronics?&quot; or &quot;What is the best microcontroller for beginners?&quot;. This post will aim to address that.</p> +<p>If you've seen my <em>About</em> page in this blog, I've discussed a possible set of microcontrollers to keep in his/her skillset as a hobbyiest. Here we will elaborate.</p> +<p>There are wide range of microcontrollers, here are some popular microcontrollers that are great choices for beginners:</p> +<ol> +<li> +<p><strong>Arduino:</strong> Arduino is widely considered one of the best options for beginners. It offers a user-friendly, open-source platform with a simple programming environment. The Arduino community is large and supportive, and there are numerous tutorials and projects available online. The Arduino Uno and Arduino Nano are excellent starter boards.</p> +</li> +<li> +<p><strong>Raspberry Pi Pico:</strong> The Raspberry Pi Pico is a microcontroller board from the Raspberry Pi Foundation. It's based on the RP2040 microcontroller and is excellent for beginners. It supports the MicroPython programming language, which is easy to learn and use. The Pico is affordable and has plenty of online resources.</p> +</li> +<li> +<p><strong>Mbed platform:</strong> Then there is also the Mbed platform, developed by Arm, a solid choice for those who want to get into more professional-grade microcontroller programming. Mbed provides a free online IDE and supports a wide range of development boards.</p> +</li> +<li> +<p><strong>Micro:bit:</strong> The BBC Micro:bit is designed for education and is an excellent choice for beginners, especially in a classroom setting. It has a user-friendly block-based programming environment but can also be programmed in Python and JavaScript. It's ideal for teaching programming and electronics concepts.</p> +</li> +</ol> +<p>Many take the Arduino route. Although this may be a good idea; in this blog we're not concerned with Arduino; these are heavy frameworks. Simple, plain and close to metal frameworks are more performant and more importantly - exciting.</p> +<p>Hence my suggestion is to begin with the widely available and inexpensive <a rel="nofollow noreferrer" href="https://web.archive.org/web/20190428082446/http://wiki.stm32duino.com/index.php?title=Blue_Pill">blue-pill board</a>. It is powered by the ST's ARM Cortex-M3 stm32f103c8; A chip that is widely used in the industry. It's got enough RAM and flash to run your initial projects while you pick up some embedded skills.</p> +<p>Pairing it up with this book: <a rel="nofollow noreferrer" href="https://www.amazon.com/BEGINNING-STM32-DEVELOPING-LIBOPENCM3-Paperback/dp/1484245970">Beginning STM32</a>, will give you a great start with step-by-step hand-holding. This book uses the <a rel="nofollow noreferrer" href="https://github.com/libopencm3/libopencm3">libopenCM3</a> HAL (that's Hardware Abstraction Layer) framework. In my opinion this framework is far superior to the ST's default cube HAL which is a botched mess.</p> +<p><img src="/img/begin_stm32.jpg" alt="Begining STM32 cover" /></p> +<p>To complement your microcontroller board and to do projects, you will need a set of commonly used electronic components. Fortunately one may find many <em>electronic starter-kits</em> on AliExpress that will suffice for a beginner hobbyist.</p> +<p>Finally you need a multimeter. Again Ali has you covered here. Note that the accuracy of the multimeter is denoted in <em>counts</em>. 4000 counts would probably be about right for hobbyist use; but the more the better.. See <a rel="nofollow noreferrer" href="https://instrumentationtools.com/multimeter-digits-counts/">here</a> for an explanation. See <a href="https://ntn888.github.io/blog/multimeters/">here</a> for a breakdown of various options.</p> +<p><a href="https://ntn888.github.io/blog/building-lab/">This post</a> works through building an economical home lab.</p> + + + + Comparison of SSG frameworks + 2023-11-05T15:27:00+00:00 + 2023-11-05T15:27:00+00:00 + + https://ntn888.github.io/blog/ssg-comparison/ + <p>Static site generators (SSGs) have gained popularity in recent years as a way to create fast, secure, and easily maintainable websites. In this article, we will compare some popular static site generator software options, highlighting their features, use cases, and benefits.</p> +<h2 id="what-is-a-static-site-generator">What is a Static Site Generator?<a class="zola-anchor" href="#what-is-a-static-site-generator" aria-label="Anchor link for: what-is-a-static-site-generator">#</a></h2> +<p>A static site generator is a tool that takes content, typically in the form of text files or data, and templates, and generates a complete website composed of HTML, CSS, and JavaScript files. Unlike dynamic websites, static sites do not rely on server-side processing or a database to generate pages. This simplicity results in faster loading times, better security, and easy scalability.</p> +<h3 id="1-jekyll">1. Jekyll<a class="zola-anchor" href="#1-jekyll" aria-label="Anchor link for: 1-jekyll">#</a></h3> +<p><strong>Key Features:</strong></p> +<ul> +<li>Written in Ruby.</li> +<li>Great for personal blogs and simple websites.</li> +<li>Extensible through plugins.</li> +<li>GitHub Pages integration.</li> +<li>Markdown and Liquid templating support.</li> +</ul> +<p><strong>Pros:</strong></p> +<ul> +<li>Straightforward to set up.</li> +<li>Ideal for bloggers and developers familiar with Ruby.</li> +<li>GitHub Pages, Git integration.</li> +<li>Large community and extensive documentation.</li> +</ul> +<p><strong>Cons:</strong></p> +<ul> +<li>Limited support for complex, database-driven sites.</li> +</ul> +<h3 id="2-hugo">2. Hugo<a class="zola-anchor" href="#2-hugo" aria-label="Anchor link for: 2-hugo">#</a></h3> +<p><strong>Key Features:</strong></p> +<ul> +<li>Written in Go.</li> +<li>Blazing fast build times.</li> +<li>Highly customizable.</li> +<li>Supports content in multiple formats.</li> +<li>Theming system for design flexibility.</li> +</ul> +<p><strong>Pros:</strong></p> +<ul> +<li>Exceptional speed in building sites.</li> +<li>Robust theming and templating.</li> +<li>Ideal for technical documentation and blogs.</li> +<li>Active and growing community.</li> +</ul> +<p><strong>Cons:</strong></p> +<ul> +<li>Limited dynamic functionality without external services.</li> +</ul> +<h3 id="3-gatsby">3. Gatsby<a class="zola-anchor" href="#3-gatsby" aria-label="Anchor link for: 3-gatsby">#</a></h3> +<p><strong>Key Features:</strong></p> +<ul> +<li>React-based.</li> +<li>Rich plugin ecosystem.</li> +<li>Integrates with various data sources.</li> +<li>Progressive Web App (PWA) support.</li> +<li>Excellent performance and SEO capabilities.</li> +</ul> +<p><strong>Pros:</strong></p> +<ul> +<li>Combines the power of React with static site generation.</li> +<li>Versatile, can be used for a wide range of projects.</li> +<li>Extensive plugin library.</li> +<li>SEO-friendly and fast load times.</li> +<li>Real-time data updates using GraphQL.</li> +</ul> +<p><strong>Cons:</strong></p> +<ul> +<li>Steeper learning curve for beginners.</li> +</ul> +<h3 id="4-hexo">4. Hexo<a class="zola-anchor" href="#4-hexo" aria-label="Anchor link for: 4-hexo">#</a></h3> +<p><strong>Key Features:</strong></p> +<ul> +<li>Written in Node.js.</li> +<li>Great for bloggers and developers.</li> +<li>Easy theming and plugin system.</li> +<li>Markdown and EJS templating support.</li> +<li>Speedy build times.</li> +</ul> +<p><strong>Pros:</strong></p> +<ul> +<li>Simple and quick to set up.</li> +<li>Node.js ecosystem and npm packages.</li> +<li>Ideal for personal blogs.</li> +<li>Support for deploying to various hosting platforms.</li> +</ul> +<p><strong>Cons:</strong></p> +<ul> +<li>Smaller community compared to some other options.</li> +</ul> +<h3 id="5-eleventy-11ty">5. Eleventy (11ty)<a class="zola-anchor" href="#5-eleventy-11ty" aria-label="Anchor link for: 5-eleventy-11ty">#</a></h3> +<p><strong>Key Features:</strong></p> +<ul> +<li>Written in JavaScript.</li> +<li>Highly flexible and configurable.</li> +<li>Supports multiple template languages.</li> +<li>Easy integration with various data sources.</li> +<li>Robust performance.</li> +</ul> +<p><strong>Pros:</strong></p> +<ul> +<li>No boilerplate code, minimal configuration.</li> +<li>Versatile, suitable for many types of projects.</li> +<li>Comprehensive documentation.</li> +<li>Strong performance and flexibility.</li> +</ul> +<p><strong>Cons:</strong></p> +<ul> +<li>Smaller community compared to older SSGs.</li> +</ul> +<h2 id="choosing-the-right-ssg">Choosing the Right SSG<a class="zola-anchor" href="#choosing-the-right-ssg" aria-label="Anchor link for: choosing-the-right-ssg">#</a></h2> +<p>The choice of the best static site generator depends on your project's requirements and your familiarity with the underlying technologies. Each SSG has its unique strengths, and you should consider factors like build speed, content sources, theming capabilities, and the size of the community when making your decision.</p> +<p>This site as pointed out <a href="https://ntn888.github.io/blog/zola-switch/">before</a> uses the Zola SSG framework. Which was separately reviewed in <a href="https://ntn888.github.io/blog/zola-review/">this post</a>.</p> +<p>In conclusion, static site generators offer a compelling solution for web developers looking to build fast, secure, and easily maintainable websites. Whether you're a blogger, technical writer, or a developer working on a project, there's an SSG that can fit your needs. Choose the one that aligns with your project's goals, and you'll enjoy the benefits of static site generation.</p> +<p>Besides writing in markdown, with git commiting, gives a streamlined blogging experience, where you get to solely focus on the <em>content</em>. And on the other hand, this ecosystem of tools you use, makes you feel like a true hacker which adds to the experience and promotes delivering more content. Which for me personally has been a key motivating factor to maintain this blog.</p> + + + + Review of Zola + 2023-11-05T06:27:00+00:00 + 2023-11-05T06:27:00+00:00 + + https://ntn888.github.io/blog/zola-review/ + <p>As revealed in the previous <a href="https://ntn888.github.io/blog/zola-switch/">post</a> this site is now migrated into the Zola SSG management system. Zola is based on rust and boasts performance. What really caught my eye though is the numerous readymade <em>light-weight</em> themes available for it. Which is a refreshing change from the heavy theme this site used previously in the Jekyll framework. With its simplicity and performance, Zola has gained recognition among developers and content creators as a powerful tool for creating websites with minimal effort.</p> +<p>Its key features are speed and efficiency; flexible theming; built-in shortcodes; taxonomies and sections.</p> +<p><strong>Some of it's other pros are:</strong></p> +<ol> +<li> +<p><strong>Rust Powered:</strong> The use of Rust makes Zola incredibly fast and robust, ensuring that your site will be blazingly quick and reliable.</p> +</li> +<li> +<p><strong>Simple Setup:</strong> Zola is easy to set up and use, making it a great choice for both newcomers to static site generation and experienced developers.</p> +</li> +<li> +<p><strong>Great Documentation:</strong> Zola provides extensive documentation that covers all aspects of the tool, making it accessible and user-friendly.</p> +</li> +<li> +<p><strong>Active Development:</strong> The Zola project is actively maintained, with regular updates and improvements, ensuring it stays up to date with modern web development trends.</p> +</li> +<li> +<p><strong>Community and Support:</strong> Zola has an active community and forum, offering support and assistance to users.</p> +</li> +</ol> +<p><strong>Cons:</strong></p> +<ol> +<li> +<p><strong>Limited theme options:</strong> Zola has a limited number of pre-built themes available. On the other hand most of the options here are lightweight (which is my preference).</p> +</li> +<li> +<p><strong>Learning Curve:</strong> Zola is known to have a steeper learning curve, but in my experience it is on par with other SSGs such as Jekyll.</p> +</li> +</ol> +<p>Zola is an impressive static site generator that combines the power of Rust with user-friendliness, resulting in a tool that's efficient and flexible. Its speed, extensibility, and built-in features make it an excellent choice for developers and content creators looking to build modern, performant static websites. While it may not have the largest ecosystem, Zola's strengths more than make up for it.</p> +<p>Many people now prefer Zola for thier SSG blog; and it's a current trend to migrate to it.. Read more about Zola on their website: <a rel="nofollow noreferrer" href="https://www.getzola.org/">https://www.getzola.org/</a>. Various deployment methods are discussed on <a rel="nofollow noreferrer" href="https://www.getzola.org/documentation/deployment/overview/">https://www.getzola.org/documentation/deployment/overview/</a>.</p> + + + + Switched site to Zola SSG + 2023-11-03T00:00:00+00:00 + 2023-11-03T00:00:00+00:00 + + https://ntn888.github.io/blog/zola-switch/ + <p>This blog site has now switched to the Zola static site generator framework. Previously it was based on Jekyll (the default github pages framework). Zola is based on Rust and is more lightweight. The look of the new site is also more lightweight.</p> +<p>This article will also serve as a trigger for testing on-push deployment and test the auto site generation!</p> +<p>In other news I'm looking to learning a web development framework. Specifically the LAMP stack... I'll log any update here if notable.</p> + + + + Deploying an IPv6 based seedbox + 2023-09-16T00:00:00+00:00 + 2023-09-16T00:00:00+00:00 + + https://ntn888.github.io/blog/ipv6-box/ + <p>With VPS that run purely IPv6 stack being cheaper (for example <a rel="nofollow noreferrer" href="https://my.hostbrr.com/order/main/index/storage">HostBRR offerings</a>); I'm doing a write-up on how to setup a seedbox running IPv6.</p> +<blockquote> +<p>Please keep in mind, you need to have IPv6 connectivity on your home computer/internet to access your services on the VPS. To check if you do head over to <a rel="nofollow noreferrer" href="https://ip6.biz/">https://ip6.biz/</a>.</p> +</blockquote> +<p>Install docker and docker compose on your machine first. Once this is done we can then setup our services.</p> +<p>Due to some of the docker image registry sources operating in ipv4 environment, they will be unreachable from our ip6 only box! We have to manually provision a method to make the interconnection work. This is only temporary though, only till we pull our images. After that we will revert to ip6 only operation.</p> +<h2 id="change-nameservers">Change Nameservers<a class="zola-anchor" href="#change-nameservers" aria-label="Anchor link for: change-nameservers">#</a></h2> +<p>To achieve this translation, we will need to adjust our nameservers as follows (courtesy <a rel="nofollow noreferrer" href="https://nat64.net/">https://nat64.net/</a>).</p> +<pre class="z-code"><code><span class="z-text z-plain">nameserver 2a01:4f9:c010:3f02::1 +nameserver 2a00:1098:2c::1 +nameserver 2a00:1098:2b::1 +</span></code></pre> +<p>Although this is not as straight forward as it seems. The file <code>/etc/resolv.conf</code> cannot be edited by hand as it will be update to any network changes. Attempting to use the package <code>resolvconf</code> deemed fruitless for me. So I resorted to the <em>trick</em> method of editing this file:</p> +<pre class="z-code"><code><span class="z-text z-plain">sudo apt install e2fsprogs -y # install needed package +cd ~ +cp /etc/resolv.conf . # Make a backup of the original file +sudo rm -f /etc/resolv.conf +sudo vim /etc/resolv.conf # insert the above file contents as nameservers +sudo chattr +i /etc/resolv.conf +sudo systemctl restart networking +</span></code></pre> +<p>Now setup your services as needed. Docker can access and pull from any generic registries.</p> +<p>Once the docker pulls are done:</p> +<pre class="z-code"><code><span class="z-text z-plain">sudo chattr -i /etc/resolv.conf +sudo cp ~/resolv.conf /etc/resolv.conf +sudo systemctl restart networking +</span></code></pre> +<p>Now our box is back to IPv6 only state.</p> +<blockquote> +<p>One downside with this system is that you can't implement auto update of docker images using watchtower. Since once again some of the docker registeries are inaccessible.</p> +</blockquote> +<h2 id="enable-ipv6-in-docker">Enable IPv6 in Docker<a class="zola-anchor" href="#enable-ipv6-in-docker" aria-label="Anchor link for: enable-ipv6-in-docker">#</a></h2> +<p>One last concern is that you need to enable IPv6 networking in docker so that the containers can reach the outside world. This is explained in <a rel="nofollow noreferrer" href="https://docs.docker.com/config/daemon/ipv6/">Enable IPv6 support</a>. Basically you have to edit the file <code>/etc/docker/daemon.json</code>.</p> +<p>Once that's done you need to create a new IPv6 network and use that with your containers. Here's a sample compose file to run the qBittorrent image:</p> +<pre class="z-code"><code><span class="z-text z-plain">version: &quot;3&quot; +services: + qbittorrent: + image: ghcr.io/hotio/qbittorrent + container_name: qbittorrent + networks: + - ip6net # &lt;---- add the container to the network + environment: + - PUID=1000 + - PGID=1000 + - TZ=Etc/UTC + - WEBUI_PORT=8080 + volumes: + - /home/ajit/.config/appdata/qbtorrent:/config + - /home/ajit/downloads:/downloads + ports: + - 8080:8080 + - 6881:6881 + - 6881:6881/udp + restart: unless-stopped + +networks: + ip6net: + enable_ipv6: true + ipam: + config: + - subnet: 2001:0DB8::/112 + +</span></code></pre> + + + + Building a Developer's Workstation + 2023-05-23T00:00:00+00:00 + 2023-05-23T00:00:00+00:00 + + https://ntn888.github.io/blog/dev-station/ + <p>In this article we will see how we build a developer's workstation that has good overall performance and suitably compact in size.</p> +<p>Our first decision is the OS. I may be a <em>little</em> biased here. But you may have guessed that Linux is our choice of OS. As discussed in [this article]({% post_url /update/2023-05-11-debian_testing %}), Debian is unsuitable as a daily driver. We choose it's derivative <a rel="nofollow noreferrer" href="https://system76.com/pop">POP! OS</a> as our OS. It is a quite popular distro that is well maintained.</p> +<p>Hardware wise our goto is the ex-lease HP Elitedesk 800 G2 mini desktop. Although it comes in several revisions, my recommendation is to go for the G2 version (running the 6th Gen core-i5) which I think is a good compromise between cost and performance/efficiency. I believe the G4 revision is the current one as of Q2 2023, which can be purchased new direct from HP site.</p> +<p>The good thing about buying an ex-lease item (other than low cost) is that you repurpose it which would have otherwise ended up straight in a landfill. And you help out the planet which is a win-win. In addition to this, Linux loves these ex-lease systems. Being a few years old and bieng in the field for a while, linux has mature drivers for it's chipsets and peripherals.</p> +<blockquote> +<p>These HP/DELL branded office desktops are very durable systems that they pose great value even as second hand ex-lease systems. That's one thing that I observed in my stint as a repair tech at a refurbished computer store.</p> +</blockquote> +<p><a rel="nofollow noreferrer" href="https://www.ebay.com/sch/i.html?_nkw=hp+elitedesk+800+g2+mini+desktop&amp;_sacat=171957&amp;mkcid=1&amp;mkrid=711-53200-19255-0&amp;siteid=0&amp;campid=5338988127&amp;customid=g2mini&amp;toolid=10001&amp;mkevt=1">Here's a referral link to ebay</a> if you are interested to purchase this. This gets me a small commission at no extra cost to you!</p> +<h3 id="installing-software">Installing Software<a class="zola-anchor" href="#installing-software" aria-label="Anchor link for: installing-software">#</a></h3> +<p>The absolute first thing you must install in a fresh install of <em>POP! OS</em> (or any distro for that matter) is <a rel="nofollow noreferrer" href="https://ohmyz.sh/">Oh My Zsh</a>. It genuinely is your terminal on steroids. It will spruce up your terminal to a whole another level. Some of it's features include:</p> +<ul> +<li>Predictive Completion</li> +<li>Enhanced Tab completion</li> +<li>Syntax highlighting (right in the shell!)</li> +</ul> +<p><a rel="nofollow noreferrer" href="https://dev.to/kumareth/a-beginner-s-guide-for-setting-up-autocomplete-on-ohmyzsh-hyper-with-plugins-themes-47f2">This article</a> has a guide on how to install it and a couple plugins to get the above functionality.</p> +<p>Next comes the decision on a text editor. If you'd like to go the vim route, I highly recommend taking a look at [Doom Emacs]({% post_url /update/2022-11-16-doom %}). It's <em>emacs</em> but with vim keybindings. Or you could go with the ever popular VS Code.</p> +<p>Finally you need to install your development environment for the framework that you use, be it web dev (node/PHP/Python etc backend) or for embedded (Zephyr in our case). If you'd like to dive into Zephyr development, take a look of my <a rel="nofollow noreferrer" href="https://simplycreate.online/zephyr-guide/">Zephyr Guide</a>.<a rel="nofollow noreferrer" href="https://academy.nordicsemi.com/courses/nrf-connect-sdk-fundamentals/">Nordic dev academy</a> also has a great intro.</p> +<p>Have fun hackin'.</p> + + + + Go nuclear! + 2023-05-21T00:00:00+00:00 + 2023-05-21T00:00:00+00:00 + + https://ntn888.github.io/blog/green-energy/ + <p>Although this discussion is not related to <em>embedded systems</em> at all, I think it's quite relevant to the current times with the energy prices sky-rocketting.</p> +<p>When it comes to switching to Green Energy, I believe going <em>nuclear</em> is the answer. Here's why.</p> +<p>Solar and wind are trickle sources. That is they are sensitive to seasonal variations. They can be great as a complementary secondary source. Otherwise as primary source they'll require large battery storages, which will be unfavourable towards the overall carbon footprint and the prohibitting expense.</p> +<p>We've had feasible nuclear fission technology for nearly a century, and promises of <em>electricity too cheap to meter</em> almost 80 years ago... Only 10% of the world's total electric generation is powered by nuclear today (With France exclusively being the pioneers at over 70%). Why is the adoption so slow?</p> +<p><img src="/img/world-electricity-production-2022.png" alt="electric production breakdown" /></p> +<p>It'll probably have to do with the precedence set by nuclear technology in the global stage. Think <em>atomic bombings</em> of World War II. It had set such a bitter mood among the public that even decades later is hard to rid of. There is also the accidents of Chernobyl and Fukushima to blame. And the latter is rather recent (2011).</p> +<p>The technology has come a long way... And with Molten Salt Reactors, Thorium fuel is used for abundant power and are categorised as intrinsically safe<sup class="footnote-reference"><a href="#1">1</a></sup>.</p> +<p>With soaring world temperatures and energy costs there's never been more urgency in switching to <em>green energy</em>. And I think it is when you commit and deploy the technology, you get into a feedback cycle where you incrementally improve. That's when the money pours in for research and the technology matures. And when countries gain energy independance it could take a chunk off the current world's problems...</p> +<p>With all the push with solar and wind energy lately, I wish there was more enthusiasm about Nuclear power. What do you think about Nuclear power generation?</p> +<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup> +<p><a rel="nofollow noreferrer" href="https://world-nuclear.org/information-library/current-and-future-generation/molten-salt-reactors.aspx">Molten Salt Reactors</a></p> +</div> + + + + My experience with drop-ship warehousing + 2023-05-17T00:00:00+00:00 + 2023-05-17T00:00:00+00:00 + + https://ntn888.github.io/blog/drop-ship/ + <p>I had an idea to pakcage, ship and store these <a href="https://ntn888.github.io/blog/xt-zb1-bl702/">bl702 devkits</a> into a US warehouse ready to locally distribute them. This is the story of how I failed misarably at it. Basically I purchased a <em>lot</em> of 100 pieces of these devkits to be stored into a warehouse to be shipped locally in the US. Although this never translated into sales; it did have many hurdles.</p> +<p>First was in purchasing itself. Basically I couldn't purchase from the ALL FAMILIAR Aliexpress. Because I had to locally ship it into the ligistic company's site in China; I could only ship to an international address. So Aliexpress was out of the question. For Chinese Domestic purchase, the supplier had a store in Taobao. This is fully in Chinese interface. Obviously.</p> +<p>Trying to register on the PC via the web portal did not succeed due to geographical blocks by Taobao. I did manage to register on the mobile app however. But unlike on the browser there's no way to easily translate for example with the Google translate plugin. What I had to do instead painstakingly, point another phone with Google lens!</p> +<p>That's one hurdle overcome. I had to fill in the local logistical company's address and all the details holding up on the other phone.</p> +<p>It took two days for the goods to reach the logistic's warehouse in China. They arrived in a box packaged with the 100 units.</p> +<p>Now the logistics company I went with was called <a rel="nofollow noreferrer" href="https://www.nextsmartship.com/">Nextsmartship</a>. My experience was largely positive with them. There was a another caveat here. When using this logistics company, it is way cheaper to prepackage into individual ready-to-mail packages here in the Chinese warehouse than to ship in bulk first and parcel them for courier there. It worked out to be $0.99 per piece here.</p> +<p>So it would be ideal to pre-package them into appropriate package unit sizes before cargo-ing to the US: for example 1pc, 5pc and 10pc units. I also think they did a very good job of safely packaging them into individual pieces.</p> +<p>At the time I dealt with them, they did not have automated integration with 3rd party sites (such as woo-commerce which I used). So once you make a sale on your site you had to manually message them (on Whatsapp). But this never happened in my instance.</p> +<p>As you see, I think one of the requirements of selling products on your own hosted site, to even gain some exposure, you'd have to be listed on Google merchant. This is the list of shopping products you'd get when a buyer clicks on the <em>Shopping</em> tab on the Google search page.</p> +<p>I didn't realise at the time, but you'd need a complete site with &quot;Privacy Policy&quot;/&quot;Returns Policy&quot; etc to get approved in Google Merchant. Google outright dismisses your site for <em>misrepresentation</em> for this. I mis-took it for selling my products that was aimed at the Chinese market... So I never bothered to probe further on this. And the whole thing went down the drain.</p> +<p>However at this point, I myself started to get frustrated with this devkit's SDK: it's lack of documentation and more importantly the everchanging updates to the SDK that breaks backwards compatibility. As I have pointed out earlier I moved on to the Nordic nRF chips. Due to lack of hobbiest sized boards (think bluepill). I put together the <a rel="nofollow noreferrer" href="https://github.com/ntn888/833iot">833iot</a> for a self-contained module based off nRF52833 sold in Alibaba. Due to previous failures I never attempted to sell them.</p> +<p><img src="/img/wSONlPv.png" alt="833iot pcb" /></p> +<p>Even though low-power wireless and <code>Thread</code> are increasingly becoming popular, the hobbiest market is still flooded with the power-hungry WiFi chips. This protocol is so inappropriate for microcontrollers!</p> + + + + Basic commands for Network Troubleshooting + 2023-05-16T00:00:00+00:00 + 2023-05-16T00:00:00+00:00 + + https://ntn888.github.io/blog/net-commands/ + <p>In this post we will look at a couple commands most used for network troubleshooting.</p> +<h2 id="ping">ping<a class="zola-anchor" href="#ping" aria-label="Anchor link for: ping">#</a></h2> +<p><code>ping</code> is the most simplest network command that everyone is familiar with. You can use it to check if a node (or device) is connected and running. Note however, that if this feature has been disabled on the node it will not respond to pings!</p> +<p>The command takes the following format:</p> +<pre class="z-code"><code><span class="z-text z-plain">ping &lt;target_ip&gt; +</span></code></pre> +<h2 id="traceroute">traceroute<a class="zola-anchor" href="#traceroute" aria-label="Anchor link for: traceroute">#</a></h2> +<p><code>traceroute</code> is a more fancy command. It gives us the <code>ping</code> results of each hop (eg routers) along the packet's path. So it can be used to deduce where in the network the packet is failing. The command takes the following format:</p> +<pre class="z-code"><code><span class="z-text z-plain">traceroute &lt;target_ip&gt; +</span></code></pre> +<p>For example it can be used to troubleshoot problems with our selfhosted email [scenario]({% post_url /update/2022-12-27-postfix_mail %}). By running the command aimed at the Google's mail servers (for example) from our box, we can deduce if the email is failing whithin the VPS provider's network. Which would mean the provider has blocked <code>port 25</code>. In this case we would get the following behaviour.</p> +<pre class="z-code"><code><span class="z-text z-plain"># traceroute -n -T -p 25 gmail-smtp-in.l.google.com +traceroute to gmail-smtp-in.l.google.com (173.194.204.27), 30 hops max, 60 byte packets +1 192.210.145.2 0.033 ms 0.012 ms 0.011 ms +2 * * * +3 * * * +4 * * * +5 * * * +. +. +. +30 * * * +</span></code></pre> +<p>Since there is no reponse from the very next node it is likely failing within the provider's network and we can say that the provider is actively blocking it.</p> +<h2 id="nc">nc<a class="zola-anchor" href="#nc" aria-label="Anchor link for: nc">#</a></h2> +<p>This one is called netcat. And it can be used to pass messages between the computers through network sockets. If you work on building an IoT device you will find this command incredibly useful.</p> +<p>You can run it in server and client mode. To run it in server mode:</p> +<pre class="z-code"><code><span class="z-text z-plain">nc -l &lt;port&gt; +</span></code></pre> +<p>To runt it in client mode:</p> +<pre class="z-code"><code><span class="z-text z-plain">echo &quot;hello there&quot; | nc &lt;remote-ip&gt; &lt;remote-port&gt; +</span></code></pre> +<p>You get the idea. If you omit the pipe command and run only the <code>nc</code> command, you'll get an interactvie prompt. Much like a telnet session!</p> +<p>As you can see this is a great way to set up makeshift listening servers on a specific port for IoT devices ad clients to connect to and debug the communication.</p> + + + + My experience with Debian Testing + 2023-05-11T00:00:00+00:00 + 2023-05-11T00:00:00+00:00 + + https://ntn888.github.io/blog/debian-testing/ + <p>I'm a big proponent of Linux [Or GNU/Linux to be technically correct]. I like the idea of how the entire system is just a collection of individual functioning pieces of software.</p> +<p>Their motto is <em>Do one thing and do it well</em>. I think this is a great mantra for successful engineering altogether. That is, break down the task into simple manageable tasks and implement and integrate them!</p> +<p>Although it's not as popular in desktops and personal computer markets, it unanimously powers the web. A good portion of servers run linux. This is no surprise considering the reliability and flexibility of the Linux OS. To probe further, the popularity of the humble Wordpress platform drives the need for Linux web servers, as Wordpress is run on top the old school LAMP stack (Where <em>L</em> denotes the Linux part).</p> +<p>Among Linux servers, Debian is one of the most popular<sup class="footnote-reference"><a href="#1">1</a></sup>. The mainline Debian distribution is considered to be the hallmark of stability. That's great. But it comes with a downside... It's software repository is not the most upto-date. Usually you'd be pulling in updates that are lagging around two years behind the current status. So it seems Debian is not so great for powering a daily driver personal workstation.</p> +<p>Enter Debian Testing. Debian has two other stable stuatus of releases: <code>Unstable</code> and <code>Testing</code>. Unstable was too unstable for me. <code>Testing</code> seemed like a good middle-ground. You can read more about <code>Testing</code> <a rel="nofollow noreferrer" href="https://wiki.debian.org/DebianTesting">here</a>. One of the pre-requisites is that the package to make it into <code>Testing</code> must have been in Unstable for atleast two days.</p> +<p>One major problem with these <code>Unstable</code>/<code>Testing</code> releases is that they tend to unexpectedly break software when updating. But there are ways to fortify testing. We do this with an application called <a rel="nofollow noreferrer" href="https://github.com/linuxmint/timeshift">Timeshift</a>. As the name implies, we can backup the hard drive contents and revert the system to a previous state following an undesired outcome.</p> +<p>Note that <code>Timeshift</code> requires the partition to be formatted in <code>btrfs</code> type and that too in the right naming scheme. The following Youtube video has instructions on installing <code>Debian Testing</code> this proper way with <code>Timeshift</code> enabled if you want to try it out.</p> +<div > + <iframe + src="https://www.youtube.com/embed/IdqkjpsyUNg" + webkitallowfullscreen + mozallowfullscreen + allowfullscreen> + </iframe> +</div> +<p>In my experience it wasn't good enough. I found myself reaching too often to the Timeshift backups, from things breaking after an update. Very often I had the problem of installed applications going missing.</p> +<p>Hence I just use POP-OS. It comes from the Debian lineage, so we have familarity there especially with the package manager <code>apt</code>. It's a very popular desktop based distribution. However I'm not found of it's <code>Cosmic</code> Desktop. I find it a butchered up version of GNOME desktop. To install the GNOME desktop I just have to run <code>sudo apt install gnome-session</code>. I run trusty Debian for servers and VPSs (like we did in the other post about Servarr as a Docker host!).</p> +<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup> +<p><a rel="nofollow noreferrer" href="https://w3techs.com/technologies/details/os-linux">https://w3techs.com/technologies/details/os-linux</a></p> +</div> + + + + DIY NAS + 2023-05-10T00:00:00+00:00 + 2023-05-10T00:00:00+00:00 + + https://ntn888.github.io/blog/diy-nas/ + <p>Apart from having a local collection, the main advantage of an automated servarr (sonarr/radarr) setup is the possiblilty of auto-populating a <em>list</em> of titles based on a certain criteria. For example: all 'Marvel' films. In this article we will see how to build such a setup.</p> +<p>I think running a DIY NAS is a great hobby. This is an excellent exercise in selfhosting linux servers with the aim of maintaining them. And servarr for that matter is reasonably complex to integrate; you'll get a good feel of building docker services that will open the door to many other selfhosted services!</p> +<h1 id="considerations-for-our-setup">Considerations for our setup<a class="zola-anchor" href="#considerations-for-our-setup" aria-label="Anchor link for: considerations-for-our-setup">#</a></h1> +<h3 id="arm-sbc-vs-x86-powered-machine">Arm SBC vs x86 powered machine<a class="zola-anchor" href="#arm-sbc-vs-x86-powered-machine" aria-label="Anchor link for: arm-sbc-vs-x86-powered-machine">#</a></h3> +<p>This is the primary decision in our DIY NAS build. While SBC's are more power efficient, the high capable processors are expensive which is a requirement for direct media streaming using <code>plex</code>/<code>emby</code>. The x86 alternative is a cheaper route... Especially if you go with a second hand ex-lease SFF(small form-factor) machine. Which is what I did. These come in a great discounts on ebay seasonally (as low as $50), get one with 8G of memory and atleast a core-i5 processor. As compared to an SBC unit, these alredy come in a sturdy case to safely house your HDDs. (They can take a max of 2 storage HDDs).</p> +<p>You have lots of choices here too... Dell Optiplex 3020 to HP Elitedesk G1/G2 and so on.</p> +<p>-<a rel="nofollow noreferrer" href="https://www.ebay.com/sch/i.html?_nkw=hp+elitedesk+800+g1+sff&amp;_sacat=171957&amp;mkcid=1&amp;mkrid=711-53200-19255-0&amp;siteid=0&amp;campid=5338988127&amp;customid=hpg1&amp;toolid=10001&amp;mkevt=1">HP Elitedesk 800 G1</a></p> +<p>-<a rel="nofollow noreferrer" href="https://www.ebay.com/sch/i.html?_nkw=hp+elitedesk+800+g2+sff&amp;_sacat=171957&amp;LH_TitleDesc=0&amp;_odkw=hp+elitedesk+800+g1+sff&amp;_osacat=171957&amp;mkcid=1&amp;mkrid=711-53200-19255-0&amp;siteid=0&amp;campid=5338988127&amp;customid=hpg2&amp;toolid=10001&amp;mkevt=1">HP Elitedesk 800 G2</a></p> +<p>-<a rel="nofollow noreferrer" href="https://www.ebay.com/sch/i.html?_nkw=dell+optiplex+3020+sff&amp;_sacat=171957&amp;LH_TitleDesc=0&amp;_odkw=hp+elitedesk+800+g2+sff&amp;_osacat=171957&amp;mkcid=1&amp;mkrid=711-53200-19255-0&amp;siteid=0&amp;campid=5338988127&amp;customid=dellsff&amp;toolid=10001&amp;mkevt=1">Dell Optiplex 3020</a></p> +<p>I personally use the first one... +(Note that the above are affiliate links to ebay, where I get a small commission if you purchase through them)</p> +<h3 id="os">OS<a class="zola-anchor" href="#os" aria-label="Anchor link for: os">#</a></h3> +<p>Off the shelf (eg. freeNAS) vs plain Debian. Going with freeNAS we get the advantage of preconfigured easy RAID setup, but for our simple setup we won't use any RAID. Maybe just one or two HDDs (as our case supports) joined with the <em>Logical Volume Manager (LVM)</em> filesystem. +The advantage of going with plain Debian in my opnion (other than flexibility) is that you don't have to put up with pesky UIs for tasks that can be easily achieved via the terminal. Be warned though, since we are managing a headless Linux system remotely this will be a lot of terminal commands through SSH session.</p> +<h1 id="setting-up-services">Setting up Services<a class="zola-anchor" href="#setting-up-services" aria-label="Anchor link for: setting-up-services">#</a></h1> +<p>In my setup I have a 2.5&quot; HDD for the boot Debian drive, and a separate 3.5&quot; 4TB HDD for data storage, which like mentioned above expandable with LVM. This drive will be mounted to <code>/mnt/main</code> (I named the drive as <code>main</code> in the LVM). You will see reference to this drive path below when we configure our services.</p> +<p>Makesure you set an identifiable hostname to your Debian install, and to access it via this name you will have to install <code>avahi-daemon</code>:</p> +<pre data-lang="bash" class="language-bash z-code"><code class="language-bash" data-lang="bash"><span class="z-source z-shell z-bash"><span class="z-meta z-function-call z-shell"><span class="z-variable z-function z-shell">sudo</span></span><span class="z-meta z-function-call z-arguments z-shell"> apt install avahi-daemon</span> +</span></code></pre> +<blockquote> +<p>At this point you can detach from keyboard and access you machine remotely... Using SSH: <code>ssh &lt;user&gt;@&lt;your_hostname&gt;</code>.</p> +</blockquote> +<p>Next install <code>docker</code> and <code>docker-compose</code>. Using the guides <a rel="nofollow noreferrer" href="https://ddev.readthedocs.io/en/stable/users/install/docker-installation/">here</a> and <a rel="nofollow noreferrer" href="https://docs.docker.com/compose/install/linux/#install-using-the-repository">here</a> respectively. There's instructions on those guides to setup the relevant repositories for Debian.</p> +<h3 id="dockstarter">DockStarter<a class="zola-anchor" href="#dockstarter" aria-label="Anchor link for: dockstarter">#</a></h3> +<p>If you are new to Docker services, a great place to start is the aptly named DockStarter. It is an application with settings preconfigured and the Docker commands have been made under-the-hood. You just make selections through the menulist wizard to enable/disable services. And it comes with a vast majority of services too. See <a rel="nofollow noreferrer" href="https://github.com/GhostWriters/DockSTARTer/tree/master/compose/.apps">here</a>. It does cover most use cases of a Sonarr/Radarr setup!</p> +<p>In my opnion, DockStarter is great to understanding the mechanics of how Docker works without first getting bogged down with configuration details. Once you have understood this concept I encourage you to move on.</p> +<blockquote> +<p>Do keep in mind DockStarter is a well polished piece of software, which is still handy for simple one-container deployments... In our case we have a whole host of containers interacting with each other, which is difficult to deploy even with DockStarter.</p> +<p>See <a rel="nofollow noreferrer" href="https://trash-guides.info/Hardlinks/How-to-setup-for/Dockstarter/">here</a> if you want to setup Servarr using DockStarter. This site has good information for all kinds of Servarr installs, so you could always refer here no matter which route you take!</p> +</blockquote> +<p>Here we will be looking at plain docker-compose method of setting up services. The first course of action is to prepare our data drive.</p> +<h3 id="preparing-the-drive">Preparing the drive<a class="zola-anchor" href="#preparing-the-drive" aria-label="Anchor link for: preparing-the-drive">#</a></h3> +<p>First we need to identify the disk ID. Run <code>sudo fdisk -l | grep -i /dev/sd</code>; in my instance for example:</p> +<pre class="z-code"><code><span class="z-text z-plain">Disk /dev/sdc: 3.64 TiB, 4000787030016 bytes, 7814037168 sectors &lt;=== my 4T drive! +/dev/sdc1 2048 7814037134 7814035087 3.6T Linux filesystem +Disk /dev/sdb: 1.82 TiB, 2000398934016 bytes, 3907029168 sectors +/dev/sdb1 2048 3907029134 3907027087 1.8T Linux filesystem +Disk /dev/sda: 149.05 GiB, 160041885696 bytes, 312581808 sectors +/dev/sda1 2048 1050623 1048576 512M EFI System +/dev/sda2 1050624 310579199 309528576 147.6G Linux filesystem +/dev/sda3 310579200 312580095 2000896 977M Linux swap +</span></code></pre> +<p>We can see above that <code>dev/sdc</code> is our drive! Continue to follow instructions on formatting the drive <a rel="nofollow noreferrer" href="https://www.linuxtechi.com/how-to-create-lvm-partition-in-linux/">here</a>.</p> +<p>Finally add this line to <code>/etc/fstab</code> to enable automount:</p> +<pre class="z-code"><code><span class="z-text z-plain">/dev/vgmain/lvmain /mnt/main ext4 defaults,user 0 1 +</span></code></pre> +<p>Note that you will need to create the <code>main</code> directory in <code>/mnt</code>. And note that I named my volume group, <code>vgmain</code> and the logical volume <code>lvmain</code>.</p> +<p>Once we have formatted the drive and have made provisions to auto-mount it; the last step is to create the directory structre that will hold our data.</p> +<pre class="z-code"><code><span class="z-text z-plain">media +├── torrents +│ ├── movies +│ ├── music +│ └── tv +├── usenet +│ ├── movies +│ ├── music +│ └── tv +└── medialibrary + ├── movies + ├── music + └── tv +</span></code></pre> +<p>Run the following commands to create the above structure:</p> +<pre data-lang="bash" class="language-bash z-code"><code class="language-bash" data-lang="bash"><span class="z-source z-shell z-bash"><span class="z-meta z-function-call z-shell"><span class="z-support z-function z-cd z-shell">cd</span></span><span class="z-meta z-function-call z-arguments z-shell"> /mnt/main</span> +<span class="z-meta z-function-call z-shell"><span class="z-variable z-function z-shell">mkdir</span></span><span class="z-meta z-function-call z-arguments z-shell"> media</span> +<span class="z-meta z-function-call z-shell"><span class="z-support z-function z-cd z-shell">cd</span></span><span class="z-meta z-function-call z-arguments z-shell"> media</span> +<span class="z-meta z-function-call z-shell"><span class="z-variable z-function z-shell">mkdir</span></span><span class="z-meta z-function-call z-arguments z-shell"><span class="z-variable z-parameter z-option z-shell"><span class="z-punctuation z-definition z-parameter z-shell"> -</span>p</span> <span class="z-meta z-group z-expansion z-brace z-shell"><span class="z-punctuation z-section z-expansion z-brace z-begin z-shell">{</span>torrents<span class="z-punctuation z-separator z-shell">,</span>usenet<span class="z-punctuation z-separator z-shell">,</span>medialibrary<span class="z-punctuation z-section z-expansion z-brace z-end z-shell">}</span></span>/<span class="z-meta z-group z-expansion z-brace z-shell"><span class="z-punctuation z-section z-expansion z-brace z-begin z-shell">{</span>movies<span class="z-punctuation z-separator z-shell">,</span>tv<span class="z-punctuation z-section z-expansion z-brace z-end z-shell">}</span></span></span> +</span></code></pre> +<h3 id="download-source">Download source<a class="zola-anchor" href="#download-source" aria-label="Anchor link for: download-source">#</a></h3> +<p>Before we proceed, we have one more decision to make, regarding our download method to acquire content. There are three options:</p> +<ul> +<li>Torrent</li> +<li>Usenet</li> +<li>Debrid</li> +</ul> +<p>We wont use torrents. Despite it being free, it's a network hog.</p> +<p>Debrid is a service that consolidates numerous hosting services under a single service. Thereby you can download in premium from all supported hoster just by logging into Real-Debrid. They now also have the option of direct downloading a cached torrent source. This is what makes it possible to use it in the servarr setup. To use it you need to enable the services <code>jackett</code> and <code>rdt-client</code> below in the 'downloaders' section.</p> +<p>Here we will use <em>usenet</em> as our download source. More instructions on setting up usenet is presented in [this article]({% post_url /bl/2022-02-23-basic-gpio %}). To use this we have enabled <code>nzbget</code> as our downloader below.</p> +<h3 id="docker-compose-config">docker-compose config<a class="zola-anchor" href="#docker-compose-config" aria-label="Anchor link for: docker-compose-config">#</a></h3> +<p>Make a new directory called <code>servarr</code> in your home directory to house the docker-compose config. This file will gather the configurations of all the services into one file. Once you have made this file you just have to execute <code>sudo docker compose up -d</code> within this directory in your bash shell. And after a few minutes the services will be accessible from their specified ports from the server's IP address. Here's my <code>docker-compose.yml</code> placed inside of that directory:</p> +<pre data-lang="yaml" class="language-yaml z-code"><code class="language-yaml" data-lang="yaml"><span class="z-source z-yaml"><span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">version</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-quoted z-double z-yaml"><span class="z-punctuation z-definition z-string z-begin z-yaml">&quot;</span>2.1<span class="z-punctuation z-definition z-string z-end z-yaml">&quot;</span></span> +<span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">services</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">organizr</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">image</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">linuxserver/organizr</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">container_name</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">organizr</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">environment</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PUID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PGID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">TZ=Europe/London</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">volumes</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/config/Organizr:/config</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">ports</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">80:80</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">restart</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">unless-stopped</span> + + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">filebrowser</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">image</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">hurlenko/filebrowser</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">environment</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PUID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PGID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">TZ=Europe/London</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">ports</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">443:8080</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">volumes</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main:/data</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/config/fb:/config</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">restart</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">always</span> + + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">syncthing</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">image</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">syncthing/syncthing</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">container_name</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">syncthing</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">hostname</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">mediasrv</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">environment</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PUID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PGID=1000</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">volumes</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/media:/var/syncthing</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">ports</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">8384:8384</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">22000:22000/tcp</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">22000:22000/udp</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">restart</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">unless-stopped</span> + + + + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">radarr</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">container_name</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">radarr</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">image</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">ghcr.io/hotio/radarr:latest</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">restart</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">unless-stopped</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">logging</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">driver</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">json-file</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">ports</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">7878:7878</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">environment</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PUID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PGID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">TZ=Europe/London</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">volumes</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/etc/localtime:/etc/localtime:ro</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/config/radarr:/config</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/media:/data</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">sonarr</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">container_name</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">sonarr</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">image</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">ghcr.io/hotio/sonarr:latest</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">restart</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">unless-stopped</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">logging</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">driver</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">json-file</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">ports</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">8989:8989</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">environment</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PUID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PGID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">TZ=Europe/London</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">volumes</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/etc/localtime:/etc/localtime:ro</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/config/sonarr:/config</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/media:/data</span> + + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">lidarr</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">image</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">lscr.io/linuxserver/lidarr</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">container_name</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">lidarr</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">environment</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PUID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PGID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">TZ=Europe/London</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">volumes</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/config/lidarr:/config</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/media:/data</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">ports</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">8686:8686</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">restart</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">unless-stopped</span> + +<span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> downloaders ========================================================================= +</span> + +<span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> jackett: +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> image: ghcr.io/linuxserver/jackett +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> container_name: jackett +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> environment: +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - PUID=1000 +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - PGID=1000 +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - TZ=Europe/London +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - AUTO_UPDATE=true #optional +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> #- RUN_OPTS=&lt;run options here&gt; #optional +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> volumes: +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - /mnt/main/config/jackett:/config +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - /dev/null:/downloads +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> ports: +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - 9117:9117 +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> restart: unless-stopped +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> rdtclient: +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> container_name: rdtclient +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> environment: +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - PUID=1000 +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - PGID=1000 +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - TZ=Europe/London +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> volumes: +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - &#39;/mnt/main/media/torrents/downloads:/data/downloads&#39; +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - &#39;/mnt/main/config/rdt-client/:/data/db&#39; +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> image: rogerfar/rdtclient +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> restart: always +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> logging: +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> driver: json-file +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> options: +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> max-size: 10m +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> ports: +</span><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - &#39;6500:6500&#39; +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> bittorrent: +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> image: ghcr.io/linuxserver/qbittorrent +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> container_name: qbittorrent +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> environment: +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - PUID=1000 +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - PGID=1000 +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - TZ=Europe/London +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - WEBUI_PORT=8080 +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> volumes: +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - /mnt/main/config/qbittorrent:/config +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - /mnt/main/media/torrents:/data/torrents:rw +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> ports: +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - 6881:6881 +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - 6881:6881/udp +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> - 8080:8080 +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> restart: unless-stopped +</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> +</span> <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">nzbget</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">container_name</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">nzbget</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">image</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">ghcr.io/linuxserver/nzbget</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">restart</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">unless-stopped</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">logging</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">driver</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">json-file</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">ports</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">6789:6789</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">environment</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PUID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PGID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">TZ=Europe/London</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">volumes</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/etc/localtime:/etc/localtime:ro</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/config/nzbget:/config</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/media/usenet:/data/usenet:rw</span> + + +<span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">networks</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">default</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">external</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">name</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">mynet</span> +</span></code></pre> +<p>Note the ports. You need them to access the individual services. For example to access Radarr point to <code>http://&lt;your_server&gt;:7878</code>. On the default port (80) we have configured Organizr. Which is a bookmarker to other services to easily access them. Read more about this <a rel="nofollow noreferrer" href="https://docs.organizr.app/">here</a>. Note that in the config above, I have added <code>filebrowser</code> and <code>syncthing</code> services in case they become handy.</p> +<h3 id="configuring-downloader">Configuring Downloader<a class="zola-anchor" href="#configuring-downloader" aria-label="Anchor link for: configuring-downloader">#</a></h3> +<p>See above in <a href="https://ntn888.github.io/blog/diy-nas/#download-source">Download source</a> for more information on this.</p> +<h3 id="configuring-radarr">Configuring Radarr<a class="zola-anchor" href="#configuring-radarr" aria-label="Anchor link for: configuring-radarr">#</a></h3> +<p>See the separate post [Configuring Radarr]({% post_url /bl/2022-02-23-basic-gpio %}) on how to do this. <a rel="nofollow noreferrer" href="https://trash-guides.info/Radarr/">Trash Guides</a> is very handy.</p> +<p>Repeat the same steps with Sonarr as well to configure it.</p> +<h3 id="media-streamer">Media Streamer<a class="zola-anchor" href="#media-streamer" aria-label="Anchor link for: media-streamer">#</a></h3> +<p>The one thing missing in the above config is the media streamer app. This is the service that let's you directly play content from your browser! Due to it's independant nature from the other apps I decided to keep this in a separate file. So we can easily bring up or down our servarr services together independent of the streaming service.</p> +<p>You can choose from three vendors:</p> +<ul> +<li>Plex</li> +<li>Emby</li> +<li>Jellyfin</li> +</ul> +<p>We will choose Emby. So in your home folder again create a new directory and name it <code>emby</code>. Inside it put the following file <code>docker-compose.yml</code>:</p> +<pre data-lang="yaml" class="language-yaml z-code"><code class="language-yaml" data-lang="yaml"><span class="z-source z-yaml"><span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">version</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-quoted z-double z-yaml"><span class="z-punctuation z-definition z-string z-begin z-yaml">&quot;</span>3.2<span class="z-punctuation z-definition z-string z-end z-yaml">&quot;</span></span> +<span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">services</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">emby</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">image</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">emby/embyserver</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">container_name</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">emby1</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">network_mode</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">host</span> <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> Enable DLNA and Wake-on-Lan +</span> <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">environment</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">UID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">GID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">TZ=Europe/London</span> + <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span>- UMASK_SET=022 #optional +</span> <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">volumes</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/config/Emby:/config</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/media/medialibrary/tv:/data/tvshows</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/media/medialibrary/movies:/data/movies</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/media/medialibrary/music:/data/music</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">ports</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">8096:8096</span> + <span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span>- 8920:8920 #optional for secure connections +</span> <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">restart</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">unless-stopped</span> +</span></code></pre> +<h1 id="setting-up-monitoring">Setting up Monitoring<a class="zola-anchor" href="#setting-up-monitoring" aria-label="Anchor link for: setting-up-monitoring">#</a></h1> +<p>Last but not least we can set up beautiful graphs and charts on server monitoring. We use Graphana with Prometheus to achieve this. Here's the <code>docker-compose.yml</code> for monitoring tools:</p> +<pre data-lang="yaml" class="language-yaml z-code"><code class="language-yaml" data-lang="yaml"><span class="z-source z-yaml"><span class="z-comment z-line z-number-sign z-yaml"><span class="z-punctuation z-definition z-comment z-line z-number-sign z-yaml">#</span> System Monitoring ========================================================================= +</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">prometheus</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">image</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">prom/prometheus</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">container_name</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">prometheus</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">environment</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PUID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PGID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">TZ=Europe/London</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">UMASK_SET=022</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">volumes</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/mnt/main/config/Prometheus/prometheus.yml:/etc/prometheus/prometheus.yml</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">ports</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">9292:9090</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">restart</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">unless-stopped</span> + + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">node-exporter</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">image</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">quay.io/prometheus/node-exporter:latest</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">container_name</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">node-exporter</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">network_mode</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">host</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">environment</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PUID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PGID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">TZ=Europe/London</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">UMASK_SET=022</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">volumes</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">/:/host:ro,rslave</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">restart</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">unless-stopped</span> + + + + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">grafana</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">image</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">grafana/grafana</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">container_name</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">grafana</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">environment</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PUID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">PGID=1000</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">TZ=Europe/London</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">UMASK_SET=022</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">GF_SECURITY_ALLOW_EMBEDDING=true</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">ports</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-punctuation z-definition z-block z-sequence z-item z-yaml">-</span> <span class="z-string z-unquoted z-plain z-out z-yaml">3000:3000</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">restart</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">unless-stopped</span> + + +<span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">networks</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">default</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">external</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> + <span class="z-string z-unquoted z-plain z-out z-yaml"><span class="z-entity z-name z-tag z-yaml">name</span></span><span class="z-punctuation z-separator z-key-value z-mapping z-yaml">:</span> <span class="z-string z-unquoted z-plain z-out z-yaml">mynet</span> +</span></code></pre> +<p>Note that you need to place a file under <code>/mnt/main/config/Prometheus/prometheus.yml</code> with the following contents:</p> +<pre class="z-code"><code><span class="z-text z-plain">global: + scrape_interval: 5s + external_labels: + monitor: &#39;node&#39; +scrape_configs: + - job_name: &#39;prometheus&#39; + static_configs: + - targets: [&#39;192.168.1.169:9292&#39;] ## IP Address of the localhost + - job_name: &#39;node-exporter&#39; + static_configs: + - targets: [&#39;192.168.1.169:9100&#39;] ## IP Address of the localhost + +</span></code></pre> +<p>Remember to change the IP address above to match the one of your server! To configure the GUI watch one of the many Youtube videos on <em>&quot;server monitor with graphana and prometheus&quot;</em>. Here's a snapshot of my Graphana view:</p> +<p><img src="/img/graphana_dash.png" alt="my grpahana snapshot" /></p> + + + + Project: Gravity Detector + 2023-05-06T00:00:00+00:00 + 2023-05-06T00:00:00+00:00 + + https://ntn888.github.io/blog/project-gravity-detector/ + <p>Here we will hookup a MPU6050 accelerometer/gyro sensor to our micro:bit to detect the orientation of the board.</p> +<p>The microbit comes with an onboard accelerometer; but for the purpose of learning the procedure we will attach an external sensor to the board. The microbit also conveniently features a 5x5 LED Matrix, which we will use as a display to indicate direction. Basically we will imitate the effect of a dot on the display to flow towards the direction of gravity.</p> +<p>Given this is our first proper project with Zephyr, this will serve as an exercise of project bring-up and its structural nature.</p> +<hr /> +<p>We begin by cloning (or copying) the basic <code>hello world</code> project. Which will be our minimal starting point.</p> +<p>Hardware wise we attach the microbit to the breakout board and then attach it and the sensor to the breadboard. Then simply wire as follows: +<img src="/img/mpu6050conn.png" alt="mpu6050 wiring diagram" /></p> +<p>We will communicate with the sensor via I^2^C bus. The actual driver file for the MPU6050 is already provided with the Zephyr sources! But we will need to provide an overlay file to reflect our custom wiring (which is the main exercise of using an external sensor). And remember to activate the modules in <code>prj.conf</code>.</p> +<p><code>&lt;zephyr_root&gt;/applications/mpu6050/boards/bbc_microbit_v2.overlay</code>:</p> +<pre class="z-code"><code><span class="z-text z-plain"> +&amp;pinctrl { + i2c1_default: i2c1_default { + group1 { + psels = &lt;NRF_PSEL(TWIM_SDA, 1, 0)&gt;, + &lt;NRF_PSEL(TWIM_SCL, 0, 26)&gt;; + }; + }; + + i2c1_sleep: i2c1_sleep { + group1 { + psels = &lt;NRF_PSEL(TWIM_SDA, 1, 0)&gt;, + &lt;NRF_PSEL(TWIM_SCL, 0, 26)&gt;; + low-power-enable; + }; + }; +}; + + +&amp;i2c1 { + compatible = &quot;nordic,nrf-twim&quot;; + status = &quot;okay&quot;; + clock-frequency = &lt;I2C_BITRATE_FAST&gt;; + + pinctrl-0 = &lt;&amp;i2c1_default&gt;; + pinctrl-1 = &lt;&amp;i2c1_sleep&gt;; + pinctrl-names = &quot;default&quot;, &quot;sleep&quot;; + /*sda-pin = &lt;34&gt;; + scl-pin = &lt;35&gt;;*/ + mpu6050@68 { + compatible = &quot;invensense,mpu6050&quot;; + reg = &lt;0x68&gt;; + status = &quot;okay&quot;; + int-gpios = &lt;&amp;gpio0 2 GPIO_ACTIVE_HIGH&gt;; + }; +}; +</span></code></pre> +<p><code>prj.conf</code>:</p> +<pre class="z-code"><code><span class="z-text z-plain">CONFIG_I2C=y +CONFIG_SENSOR=y +CONFIG_MPU6050_TRIGGER_NONE=y +CONFIG_CBPRINTF_FP_SUPPORT=y + +CONFIG_DISPLAY=y +</span></code></pre> +<p>The basic overall application structure is as follows:</p> +<pre class="z-code"><code><span class="z-text z-plain">. +├── boards +│   └── bbc_microbit_v2.overlay +├── CMakeLists.txt +├── prj.conf +└── src + └── main.c + +</span></code></pre> +<h2 id="communication-with-sensor">Communication with sensor<a class="zola-anchor" href="#communication-with-sensor" aria-label="Anchor link for: communication-with-sensor">#</a></h2> +<p>In our first iteration we will achieve basic interaction with the sensor module.</p> +<p>Luckily there is a sample application in the Zephyr: <code>&lt;zephyr_root&gt;/zephyr/samples/sensor/mpu6050/</code> directory from which we will copy the main.c contents:</p> +<pre data-lang="c" class="language-c z-code"><code class="language-c" data-lang="c"><span class="z-source z-c"><span class="z-meta z-preprocessor z-include z-c"><span class="z-keyword z-control z-import z-include z-c">#include</span> <span class="z-string z-quoted z-other z-lt-gt z-include z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&lt;</span>zephyr/kernel.h<span class="z-punctuation z-definition z-string z-end z-c">&gt;</span></span> +</span><span class="z-meta z-preprocessor z-include z-c"><span class="z-keyword z-control z-import z-include z-c">#include</span> <span class="z-string z-quoted z-other z-lt-gt z-include z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&lt;</span>zephyr/device.h<span class="z-punctuation z-definition z-string z-end z-c">&gt;</span></span> +</span><span class="z-meta z-preprocessor z-include z-c"><span class="z-keyword z-control z-import z-include z-c">#include</span> <span class="z-string z-quoted z-other z-lt-gt z-include z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&lt;</span>zephyr/drivers/sensor.h<span class="z-punctuation z-definition z-string z-end z-c">&gt;</span></span> +</span><span class="z-meta z-preprocessor z-include z-c"><span class="z-keyword z-control z-import z-include z-c">#include</span> <span class="z-string z-quoted z-other z-lt-gt z-include z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&lt;</span>stdio.h<span class="z-punctuation z-definition z-string z-end z-c">&gt;</span></span> +</span> +<span class="z-storage z-modifier z-c">static</span> <span class="z-storage z-modifier z-c">const</span> <span class="z-storage z-type z-c">char</span> <span class="z-keyword z-operator z-c">*</span><span class="z-meta z-function z-c"><span class="z-entity z-name z-function z-c">now_str</span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-storage z-type z-c">void</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-meta z-function z-c"> +</span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span></span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"> + <span class="z-storage z-modifier z-c">static</span> <span class="z-storage z-type z-c">char</span> buf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">16</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-punctuation z-terminator z-c">;</span> <span class="z-comment z-block z-c"><span class="z-punctuation z-definition z-comment z-c">/*</span> ...HH:MM:SS.MMM <span class="z-punctuation z-definition z-comment z-c">*/</span></span> + <span class="z-support z-type z-stdint z-c">uint32_t</span> now <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">k_uptime_get_32</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">unsigned</span> <span class="z-storage z-type z-c">int</span> ms <span class="z-keyword z-operator z-assignment z-c">=</span> now <span class="z-keyword z-operator z-arithmetic z-c">%</span> MSEC_PER_SEC<span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">unsigned</span> <span class="z-storage z-type z-c">int</span> s<span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">unsigned</span> <span class="z-storage z-type z-c">int</span> min<span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">unsigned</span> <span class="z-storage z-type z-c">int</span> h<span class="z-punctuation z-terminator z-c">;</span> + + now <span class="z-keyword z-operator z-assignment z-augmented z-c">/=</span> MSEC_PER_SEC<span class="z-punctuation z-terminator z-c">;</span> + s <span class="z-keyword z-operator z-assignment z-c">=</span> now <span class="z-keyword z-operator z-arithmetic z-c">%</span> <span class="z-constant z-numeric z-integer z-decimal z-c">60<span class="z-storage z-type z-numeric z-c">U</span></span><span class="z-punctuation z-terminator z-c">;</span> + now <span class="z-keyword z-operator z-assignment z-augmented z-c">/=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">60<span class="z-storage z-type z-numeric z-c">U</span></span><span class="z-punctuation z-terminator z-c">;</span> + min <span class="z-keyword z-operator z-assignment z-c">=</span> now <span class="z-keyword z-operator z-arithmetic z-c">%</span> <span class="z-constant z-numeric z-integer z-decimal z-c">60<span class="z-storage z-type z-numeric z-c">U</span></span><span class="z-punctuation z-terminator z-c">;</span> + now <span class="z-keyword z-operator z-assignment z-augmented z-c">/=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">60<span class="z-storage z-type z-numeric z-c">U</span></span><span class="z-punctuation z-terminator z-c">;</span> + h <span class="z-keyword z-operator z-assignment z-c">=</span> now<span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-meta z-function-call z-c"><span class="z-support z-function z-C99 z-c">snprintf</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">buf<span class="z-punctuation z-separator z-c">,</span> <span class="z-keyword z-operator z-word z-c">sizeof</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span><span class="z-meta z-group z-c">buf</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span><span class="z-punctuation z-separator z-c">,</span> <span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span><span class="z-constant z-other z-placeholder z-c">%u</span>:<span class="z-constant z-other z-placeholder z-c">%02u</span>:<span class="z-constant z-other z-placeholder z-c">%02u</span>.<span class="z-constant z-other z-placeholder z-c">%03u</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-punctuation z-separator z-c">,</span> + h<span class="z-punctuation z-separator z-c">,</span> min<span class="z-punctuation z-separator z-c">,</span> s<span class="z-punctuation z-separator z-c">,</span> ms</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-flow z-return z-c">return</span> buf<span class="z-punctuation z-terminator z-c">;</span> +</span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-end z-c">}</span></span></span> + +<span class="z-storage z-modifier z-c">static</span> <span class="z-storage z-type z-c">int</span> <span class="z-meta z-function z-c"><span class="z-entity z-name z-function z-c">process_mpu6050</span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-storage z-modifier z-c">const</span> <span class="z-storage z-type z-c">struct</span> device <span class="z-keyword z-operator z-c">*</span><span class="z-variable z-parameter z-c">dev</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-meta z-function z-c"> +</span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span></span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"> + <span class="z-storage z-type z-c">struct</span> sensor_value temperature<span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">struct</span> sensor_value accel<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">3</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">struct</span> sensor_value gyro<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">3</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">int</span> rc <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_sample_fetch</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>rc <span class="z-keyword z-operator z-comparison z-c">==</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + rc <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_channel_get</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev<span class="z-punctuation z-separator z-c">,</span> SENSOR_CHAN_ACCEL_XYZ<span class="z-punctuation z-separator z-c">,</span> + accel</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>rc <span class="z-keyword z-operator z-comparison z-c">==</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + rc <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_channel_get</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev<span class="z-punctuation z-separator z-c">,</span> SENSOR_CHAN_GYRO_XYZ<span class="z-punctuation z-separator z-c">,</span> + gyro</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>rc <span class="z-keyword z-operator z-comparison z-c">==</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + rc <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_channel_get</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev<span class="z-punctuation z-separator z-c">,</span> SENSOR_CHAN_AMBIENT_TEMP<span class="z-punctuation z-separator z-c">,</span> + <span class="z-keyword z-operator z-c">&amp;</span>temperature</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>rc <span class="z-keyword z-operator z-comparison z-c">==</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-support z-function z-C99 z-c">printf</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>[<span class="z-constant z-other z-placeholder z-c">%s</span>]:<span class="z-constant z-other z-placeholder z-c">%g</span> Cel<span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span> + <span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span> accel <span class="z-constant z-other z-placeholder z-c">%f</span> <span class="z-constant z-other z-placeholder z-c">%f</span> <span class="z-constant z-other z-placeholder z-c">%f</span> m/s/s<span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span> + <span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span> gyro <span class="z-constant z-other z-placeholder z-c">%f</span> <span class="z-constant z-other z-placeholder z-c">%f</span> <span class="z-constant z-other z-placeholder z-c">%f</span> rad/s<span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">now_str</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_value_to_double</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-keyword z-operator z-c">&amp;</span>temperature</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_value_to_double</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-keyword z-operator z-c">&amp;</span>accel<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_value_to_double</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-keyword z-operator z-c">&amp;</span>accel<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">1</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_value_to_double</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-keyword z-operator z-c">&amp;</span>accel<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">2</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_value_to_double</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-keyword z-operator z-c">&amp;</span>gyro<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_value_to_double</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-keyword z-operator z-c">&amp;</span>gyro<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">1</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_value_to_double</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-keyword z-operator z-c">&amp;</span>gyro<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">2</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> <span class="z-keyword z-control z-c">else</span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-support z-function z-C99 z-c">printf</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>sample fetch/get failed: <span class="z-constant z-other z-placeholder z-c">%d</span><span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-punctuation z-separator z-c">,</span> rc</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + + <span class="z-keyword z-control z-flow z-return z-c">return</span> rc<span class="z-punctuation z-terminator z-c">;</span> +</span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-end z-c">}</span></span></span> + +<span class="z-meta z-preprocessor z-c"><span class="z-keyword z-control z-import z-c">#ifdef</span> CONFIG_MPU6050_TRIGGER +</span><span class="z-storage z-modifier z-c">static</span> <span class="z-storage z-type z-c">struct</span> sensor_trigger trigger<span class="z-punctuation z-terminator z-c">;</span> + +<span class="z-storage z-modifier z-c">static</span> <span class="z-storage z-type z-c">void</span> <span class="z-meta z-function z-c"><span class="z-entity z-name z-function z-c">handle_mpu6050_drdy</span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-storage z-modifier z-c">const</span> <span class="z-storage z-type z-c">struct</span> device <span class="z-keyword z-operator z-c">*</span><span class="z-variable z-parameter z-c">dev</span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-storage z-modifier z-c">const</span> <span class="z-storage z-type z-c">struct</span> sensor_trigger <span class="z-keyword z-operator z-c">*</span><span class="z-variable z-parameter z-c">trig</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-meta z-function z-c"> +</span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span></span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"> + <span class="z-storage z-type z-c">int</span> rc <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">process_mpu6050</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>rc <span class="z-keyword z-operator z-comparison z-c">!=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-support z-function z-C99 z-c">printf</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>cancelling trigger due to failure: <span class="z-constant z-other z-placeholder z-c">%d</span><span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-punctuation z-separator z-c">,</span> rc</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span><span class="z-storage z-type z-c">void</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span><span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_trigger_set</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev<span class="z-punctuation z-separator z-c">,</span> trig<span class="z-punctuation z-separator z-c">,</span> <span class="z-constant z-language z-c">NULL</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-flow z-return z-c">return</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> +</span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-end z-c">}</span></span></span> +<span class="z-meta z-preprocessor z-c"><span class="z-keyword z-control z-import z-c">#endif</span></span> <span class="z-comment z-block z-c"><span class="z-punctuation z-definition z-comment z-c">/*</span> CONFIG_MPU6050_TRIGGER <span class="z-punctuation z-definition z-comment z-c">*/</span></span> + +<span class="z-storage z-type z-c">int</span> <span class="z-meta z-function z-c"><span class="z-entity z-name z-function z-c">main</span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-storage z-type z-c">void</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-meta z-function z-c"> +</span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span></span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"> + <span class="z-storage z-modifier z-c">const</span> <span class="z-storage z-type z-c">struct</span> device <span class="z-keyword z-operator z-c">*</span><span class="z-storage z-modifier z-c">const</span> mpu6050 <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">DEVICE_DT_GET_ONE</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">invensense_mpu6050</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span><span class="z-keyword z-operator z-arithmetic z-c">!</span><span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">device_is_ready</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">mpu6050</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-support z-function z-C99 z-c">printf</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>Device <span class="z-constant z-other z-placeholder z-c">%s</span> is not ready<span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-punctuation z-separator z-c">,</span> mpu6050<span class="z-punctuation z-accessor z-c">-&gt;</span>name</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-flow z-return z-c">return</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + +<span class="z-meta z-preprocessor z-c"><span class="z-keyword z-control z-import z-c">#ifdef</span> CONFIG_MPU6050_TRIGGER +</span> trigger <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span><span class="z-storage z-type z-c">struct</span> sensor_trigger<span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-punctuation z-accessor z-c">.</span><span class="z-variable z-other z-member z-c">type</span> <span class="z-keyword z-operator z-assignment z-c">=</span> SENSOR_TRIG_DATA_READY<span class="z-punctuation z-separator z-c">,</span> + <span class="z-punctuation z-accessor z-c">.</span><span class="z-variable z-other z-member z-c">chan</span> <span class="z-keyword z-operator z-assignment z-c">=</span> SENSOR_CHAN_ALL<span class="z-punctuation z-separator z-c">,</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span><span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_trigger_set</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">mpu6050<span class="z-punctuation z-separator z-c">,</span> <span class="z-keyword z-operator z-c">&amp;</span>trigger<span class="z-punctuation z-separator z-c">,</span> + handle_mpu6050_drdy</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span> <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-support z-function z-C99 z-c">printf</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>Cannot configure trigger<span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-flow z-return z-c">return</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">printk</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>Configured for triggered sampling.<span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span></span></span><span class="z-punctuation z-terminator z-c">;</span> +<span class="z-meta z-preprocessor z-c"><span class="z-keyword z-control z-import z-c">#endif</span></span> + + <span class="z-keyword z-control z-c">while</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span><span class="z-keyword z-operator z-arithmetic z-c">!</span><span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">IS_ENABLED</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">CONFIG_MPU6050_TRIGGER</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-storage z-type z-c">int</span> rc <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">process_mpu6050</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">mpu6050</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>rc <span class="z-keyword z-operator z-comparison z-c">!=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-keyword z-control z-flow z-break z-c">break</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">k_sleep</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">K_SECONDS</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-constant z-numeric z-integer z-decimal z-c">2</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + + <span class="z-comment z-block z-c"><span class="z-punctuation z-definition z-comment z-c">/*</span> triggered runs with its own thread after exit <span class="z-punctuation z-definition z-comment z-c">*/</span></span> + <span class="z-keyword z-control z-flow z-return z-c">return</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> +</span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-end z-c">}</span></span></span> +</span></code></pre> +<p>The output on the Zephyr serial console:</p> +<pre class="z-code"><code><span class="z-text z-plain">*** Booting Zephyr OS build zephyr-v3.3.0-3014-ge59e65dc75e4 *** +[0:00:00.008]:19.9182 Cel + accel 0.351947 -0.275334 10.371681 m/s/s + gyro -0.021583 -0.014655 -0.006794 rad/s +[0:00:02.019]:19.8712 Cel + accel 0.272938 -0.150835 10.292672 m/s/s + gyro -0.019584 -0.009592 -0.012124 rad/s +</span></code></pre> +<h2 id="output-via-led-matrix">Output via LED Matrix<a class="zola-anchor" href="#output-via-led-matrix" aria-label="Anchor link for: output-via-led-matrix">#</a></h2> +<p>Now that we have confirmed successful communication with the module, we will strip off code related to console logging. And introduce the following function:</p> +<pre data-lang="c" class="language-c z-code"><code class="language-c" data-lang="c"><span class="z-source z-c"><span class="z-storage z-type z-c">void</span> <span class="z-meta z-function z-c"><span class="z-entity z-name z-function z-c">update_mydisplay</span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-storage z-type z-c">double</span> <span class="z-variable z-parameter z-c">y</span><span class="z-punctuation z-separator z-c">,</span> <span class="z-storage z-type z-c">double</span> <span class="z-variable z-parameter z-c">z</span><span class="z-punctuation z-separator z-c">,</span> <span class="z-storage z-modifier z-c">const</span> <span class="z-storage z-type z-c">struct</span> device <span class="z-keyword z-operator z-c">*</span><span class="z-variable z-parameter z-c">dev</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-meta z-function z-c"> +</span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span></span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"> + <span class="z-comment z-line z-double-slash z-c"><span class="z-punctuation z-definition z-comment z-c">//</span> makeup buf here +</span><span class="z-comment z-block z-c"><span class="z-punctuation z-definition z-comment z-c">/*</span> buf[0] = PIXEL_MASK(1, 0, 1, 0, 1); + buf[1] = PIXEL_MASK(1, 1, 0, 0, 1); + buf[2] = PIXEL_MASK(1, 0, 1, 0, 1); + buf[3] = PIXEL_MASK(1, 0, 0, 1, 1); + buf[4] = PIXEL_MASK(1, 0, 1, 0, 1); +<span class="z-punctuation z-definition z-comment z-c">*/</span></span> + <span class="z-support z-type z-stdint z-c">uint8_t</span> tbuf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">int</span> i<span class="z-punctuation z-separator z-c">,</span> j<span class="z-punctuation z-separator z-c">,</span> ii<span class="z-punctuation z-separator z-c">,</span> jj<span class="z-punctuation z-separator z-c">,</span> ret<span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-keyword z-control z-c">for</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>i <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> i <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-terminator z-c">;</span> i<span class="z-keyword z-operator z-arithmetic z-c">++</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + <span class="z-keyword z-control z-c">for</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>j <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> j <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-terminator z-c">;</span> j<span class="z-keyword z-operator z-arithmetic z-c">++</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + tbuf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>i<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>j<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span> <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-comment z-line z-double-slash z-c"><span class="z-punctuation z-definition z-comment z-c">//</span> work the dot here +</span> jj <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span> <span class="z-keyword z-operator z-arithmetic z-c">-</span> y<span class="z-keyword z-operator z-arithmetic z-c">/</span><span class="z-constant z-numeric z-integer z-decimal z-c">10</span><span class="z-keyword z-operator z-c">*</span><span class="z-constant z-numeric z-integer z-decimal z-c">5</span> <span class="z-keyword z-operator z-arithmetic z-c">+</span> <span class="z-constant z-numeric z-integer z-decimal z-c">2</span><span class="z-punctuation z-terminator z-c">;</span> + ii <span class="z-keyword z-operator z-assignment z-c">=</span> z<span class="z-keyword z-operator z-arithmetic z-c">/</span><span class="z-constant z-numeric z-integer z-decimal z-c">10</span><span class="z-keyword z-operator z-c">*</span><span class="z-constant z-numeric z-integer z-decimal z-c">5</span> <span class="z-keyword z-operator z-arithmetic z-c">+</span> <span class="z-constant z-numeric z-integer z-decimal z-c">2</span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-comment z-line z-double-slash z-c"><span class="z-punctuation z-definition z-comment z-c">//</span> assign ceiling values to trap the dot into display! +</span> <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>ii <span class="z-keyword z-operator z-comparison z-c">&gt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">4</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + ii <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">4</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>jj <span class="z-keyword z-operator z-comparison z-c">&gt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">4</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + jj <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">4</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>ii <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + ii <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>jj <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + jj <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> + + tbuf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>ii<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>jj<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span> <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">1</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">for</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>i <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> i <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-terminator z-c">;</span> i<span class="z-keyword z-operator z-arithmetic z-c">++</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + buf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>i<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span> <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-keyword z-control z-c">for</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>i <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> i <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-terminator z-c">;</span> i<span class="z-keyword z-operator z-arithmetic z-c">++</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + <span class="z-keyword z-control z-c">for</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>j <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> j <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-terminator z-c">;</span> j<span class="z-keyword z-operator z-arithmetic z-c">++</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + buf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>i<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span> <span class="z-keyword z-operator z-assignment z-augmented z-c">|=</span> tbuf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>i<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>j<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>j <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">4</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + buf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>i<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span> <span class="z-keyword z-operator z-assignment z-augmented z-c">&lt;&lt;=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">1</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + + ret <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">display_set_brightness</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev<span class="z-punctuation z-separator z-c">,</span> <span class="z-constant z-numeric z-integer z-hexadecimal z-c"><span class="z-punctuation z-definition z-numeric z-base z-c">0x</span>7F</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>ret <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">printk</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>display_set_brightness failed: <span class="z-constant z-other z-placeholder z-c">%u</span>/<span class="z-constant z-other z-placeholder z-c">%d</span><span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-support z-constant z-c">__LINE__</span><span class="z-punctuation z-separator z-c">,</span> ret</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + ret <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">display_write</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev<span class="z-punctuation z-separator z-c">,</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-separator z-c">,</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-separator z-c">,</span> <span class="z-keyword z-operator z-c">&amp;</span>buf_desc<span class="z-punctuation z-separator z-c">,</span> buf</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>ret <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">printk</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>display_write failed: <span class="z-constant z-other z-placeholder z-c">%u</span>/<span class="z-constant z-other z-placeholder z-c">%d</span><span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-support z-constant z-c">__LINE__</span><span class="z-punctuation z-separator z-c">,</span> ret</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + + ret <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">display_blanking_off</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>ret <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">printk</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>display_blanking_off failed: <span class="z-constant z-other z-placeholder z-c">%u</span>/<span class="z-constant z-other z-placeholder z-c">%d</span><span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-support z-constant z-c">__LINE__</span><span class="z-punctuation z-separator z-c">,</span> ret</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + +</span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-end z-c">}</span></span></span> +</span></code></pre> +<p>In the code above, we use an intermediate 2d array to represent the LED Matrix to flexibly refer to our indicating dot. This <em>dot</em> will move towards the direction of flowing gravity. Now our sensor is 3-dimensional but we only consider 2 axes for the 2d array. By means of trial-and-error, this works out to be the x and z axis of the sensor, which is passed to this function.</p> +<p>Since our final LED Matrix data is single dimensional 5bit array; we need to populate this data by means of our temporary 2d array. We do this by bitwise shifting to the left while cycling through each row and copy it through.</p> +<p>And we need to periodically call this function after processing sensor data. Here's the updated main.c:</p> +<pre data-lang="c" class="language-c z-code"><code class="language-c" data-lang="c"><span class="z-source z-c"><span class="z-meta z-preprocessor z-include z-c"><span class="z-keyword z-control z-import z-include z-c">#include</span> <span class="z-string z-quoted z-other z-lt-gt z-include z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&lt;</span>zephyr/kernel.h<span class="z-punctuation z-definition z-string z-end z-c">&gt;</span></span> +</span><span class="z-meta z-preprocessor z-include z-c"><span class="z-keyword z-control z-import z-include z-c">#include</span> <span class="z-string z-quoted z-other z-lt-gt z-include z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&lt;</span>zephyr/device.h<span class="z-punctuation z-definition z-string z-end z-c">&gt;</span></span> +</span><span class="z-meta z-preprocessor z-include z-c"><span class="z-keyword z-control z-import z-include z-c">#include</span> <span class="z-string z-quoted z-other z-lt-gt z-include z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&lt;</span>zephyr/drivers/display.h<span class="z-punctuation z-definition z-string z-end z-c">&gt;</span></span> +</span><span class="z-meta z-preprocessor z-include z-c"><span class="z-keyword z-control z-import z-include z-c">#include</span> <span class="z-string z-quoted z-other z-lt-gt z-include z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&lt;</span>zephyr/drivers/sensor.h<span class="z-punctuation z-definition z-string z-end z-c">&gt;</span></span> +</span><span class="z-meta z-preprocessor z-include z-c"><span class="z-keyword z-control z-import z-include z-c">#include</span> <span class="z-string z-quoted z-other z-lt-gt z-include z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&lt;</span>stdio.h<span class="z-punctuation z-definition z-string z-end z-c">&gt;</span></span> +</span> +<span class="z-meta z-preprocessor z-macro z-c"><span class="z-keyword z-control z-import z-define z-c">#define</span></span><span class="z-meta z-preprocessor z-macro z-c"> <span class="z-entity z-name z-function z-preprocessor z-c">PIXEL_BIT</span></span><span class="z-meta z-preprocessor z-macro z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span><span class="z-variable z-parameter z-c">idx</span><span class="z-punctuation z-separator z-c">,</span> <span class="z-variable z-parameter z-c">val</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-meta z-preprocessor z-macro z-c"> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>val <span class="z-keyword z-operator z-ternary z-c">?</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">BIT</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">idx</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span> <span class="z-keyword z-operator z-ternary z-c">:</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span> +<span class="z-meta z-preprocessor z-macro z-c"><span class="z-keyword z-control z-import z-define z-c">#define</span></span><span class="z-meta z-preprocessor z-macro z-c"> <span class="z-entity z-name z-function z-preprocessor z-c">PIXEL_MASK</span></span><span class="z-meta z-preprocessor z-macro z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span><span class="z-keyword z-operator z-variadic z-c">...</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-meta z-preprocessor z-macro z-c"> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span><span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">FOR_EACH_IDX</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">PIXEL_BIT<span class="z-punctuation z-separator z-c">,</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span><span class="z-keyword z-operator z-arithmetic z-c">|</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span><span class="z-punctuation z-separator z-c">,</span> __VA_ARGS__</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span> + +<span class="z-storage z-modifier z-c">static</span> <span class="z-storage z-type z-c">struct</span> display_capabilities caps<span class="z-punctuation z-terminator z-c">;</span> +<span class="z-storage z-modifier z-c">static</span> <span class="z-support z-type z-stdint z-c">uint8_t</span> buf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-punctuation z-terminator z-c">;</span> +<span class="z-storage z-modifier z-c">static</span> <span class="z-storage z-modifier z-c">const</span> <span class="z-storage z-type z-c">struct</span> display_buffer_descriptor buf_desc <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-punctuation z-accessor z-c">.</span><span class="z-variable z-other z-member z-c">buf_size</span> <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-keyword z-operator z-word z-c">sizeof</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span><span class="z-meta z-group z-c">buf</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-punctuation z-accessor z-c">.</span><span class="z-variable z-other z-member z-c">width</span> <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-punctuation z-accessor z-c">.</span><span class="z-variable z-other z-member z-c">height</span> <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-punctuation z-accessor z-c">.</span><span class="z-variable z-other z-member z-c">pitch</span> <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">8</span><span class="z-punctuation z-separator z-c">,</span> +<span class="z-punctuation z-section z-block z-end z-c">}</span></span><span class="z-punctuation z-terminator z-c">;</span> + +<span class="z-storage z-type z-c">void</span> <span class="z-meta z-function z-c"><span class="z-entity z-name z-function z-c">update_mydisplay</span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-storage z-type z-c">double</span> <span class="z-variable z-parameter z-c">y</span><span class="z-punctuation z-separator z-c">,</span> <span class="z-storage z-type z-c">double</span> <span class="z-variable z-parameter z-c">z</span><span class="z-punctuation z-separator z-c">,</span> <span class="z-storage z-modifier z-c">const</span> <span class="z-storage z-type z-c">struct</span> device <span class="z-keyword z-operator z-c">*</span><span class="z-variable z-parameter z-c">dev</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-meta z-function z-c"> +</span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span></span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"> + <span class="z-comment z-line z-double-slash z-c"><span class="z-punctuation z-definition z-comment z-c">//</span> makeup buf here +</span><span class="z-comment z-block z-c"><span class="z-punctuation z-definition z-comment z-c">/*</span> buf[0] = PIXEL_MASK(1, 0, 1, 0, 1); + buf[1] = PIXEL_MASK(1, 1, 0, 0, 1); + buf[2] = PIXEL_MASK(1, 0, 1, 0, 1); + buf[3] = PIXEL_MASK(1, 0, 0, 1, 1); + buf[4] = PIXEL_MASK(1, 0, 1, 0, 1); +<span class="z-punctuation z-definition z-comment z-c">*/</span></span> + <span class="z-support z-type z-stdint z-c">uint8_t</span> tbuf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">int</span> i<span class="z-punctuation z-separator z-c">,</span> j<span class="z-punctuation z-separator z-c">,</span> ii<span class="z-punctuation z-separator z-c">,</span> jj<span class="z-punctuation z-separator z-c">,</span> ret<span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-keyword z-control z-c">for</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>i <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> i <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-terminator z-c">;</span> i<span class="z-keyword z-operator z-arithmetic z-c">++</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + <span class="z-keyword z-control z-c">for</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>j <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> j <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-terminator z-c">;</span> j<span class="z-keyword z-operator z-arithmetic z-c">++</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + tbuf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>i<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>j<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span> <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-comment z-line z-double-slash z-c"><span class="z-punctuation z-definition z-comment z-c">//</span> work the dot here +</span> jj <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span> <span class="z-keyword z-operator z-arithmetic z-c">-</span> y<span class="z-keyword z-operator z-arithmetic z-c">/</span><span class="z-constant z-numeric z-integer z-decimal z-c">10</span><span class="z-keyword z-operator z-c">*</span><span class="z-constant z-numeric z-integer z-decimal z-c">5</span> <span class="z-keyword z-operator z-arithmetic z-c">+</span> <span class="z-constant z-numeric z-integer z-decimal z-c">2</span><span class="z-punctuation z-terminator z-c">;</span> + ii <span class="z-keyword z-operator z-assignment z-c">=</span> z<span class="z-keyword z-operator z-arithmetic z-c">/</span><span class="z-constant z-numeric z-integer z-decimal z-c">10</span><span class="z-keyword z-operator z-c">*</span><span class="z-constant z-numeric z-integer z-decimal z-c">5</span> <span class="z-keyword z-operator z-arithmetic z-c">+</span> <span class="z-constant z-numeric z-integer z-decimal z-c">2</span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-comment z-line z-double-slash z-c"><span class="z-punctuation z-definition z-comment z-c">//</span> assign ceiling values to trap the dot into display! +</span> <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>ii <span class="z-keyword z-operator z-comparison z-c">&gt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">4</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + ii <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">4</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>jj <span class="z-keyword z-operator z-comparison z-c">&gt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">4</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + jj <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">4</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>ii <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + ii <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>jj <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + jj <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> + + tbuf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>ii<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>jj<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span> <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">1</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">for</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>i <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> i <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-terminator z-c">;</span> i<span class="z-keyword z-operator z-arithmetic z-c">++</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + buf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>i<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span> <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-keyword z-control z-c">for</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>i <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> i <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-terminator z-c">;</span> i<span class="z-keyword z-operator z-arithmetic z-c">++</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + <span class="z-keyword z-control z-c">for</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>j <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> j <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">5</span><span class="z-punctuation z-terminator z-c">;</span> j<span class="z-keyword z-operator z-arithmetic z-c">++</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + buf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>i<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span> <span class="z-keyword z-operator z-assignment z-augmented z-c">|=</span> tbuf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>i<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>j<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>j <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">4</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> + buf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span>i<span class="z-punctuation z-section z-brackets z-end z-c">]</span></span> <span class="z-keyword z-operator z-assignment z-augmented z-c">&lt;&lt;=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">1</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + + ret <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">display_set_brightness</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev<span class="z-punctuation z-separator z-c">,</span> <span class="z-constant z-numeric z-integer z-hexadecimal z-c"><span class="z-punctuation z-definition z-numeric z-base z-c">0x</span>7F</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>ret <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">printk</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>display_set_brightness failed: <span class="z-constant z-other z-placeholder z-c">%u</span>/<span class="z-constant z-other z-placeholder z-c">%d</span><span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-support z-constant z-c">__LINE__</span><span class="z-punctuation z-separator z-c">,</span> ret</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + ret <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">display_write</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev<span class="z-punctuation z-separator z-c">,</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-separator z-c">,</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-separator z-c">,</span> <span class="z-keyword z-operator z-c">&amp;</span>buf_desc<span class="z-punctuation z-separator z-c">,</span> buf</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>ret <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">printk</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>display_write failed: <span class="z-constant z-other z-placeholder z-c">%u</span>/<span class="z-constant z-other z-placeholder z-c">%d</span><span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-support z-constant z-c">__LINE__</span><span class="z-punctuation z-separator z-c">,</span> ret</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + + ret <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">display_blanking_off</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>ret <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">printk</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>display_blanking_off failed: <span class="z-constant z-other z-placeholder z-c">%u</span>/<span class="z-constant z-other z-placeholder z-c">%d</span><span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-support z-constant z-c">__LINE__</span><span class="z-punctuation z-separator z-c">,</span> ret</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + +</span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-end z-c">}</span></span></span> + +<span class="z-storage z-modifier z-c">static</span> <span class="z-storage z-modifier z-c">const</span> <span class="z-storage z-type z-c">char</span> <span class="z-keyword z-operator z-c">*</span><span class="z-meta z-function z-c"><span class="z-entity z-name z-function z-c">now_str</span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-storage z-type z-c">void</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-meta z-function z-c"> +</span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span></span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"> + <span class="z-storage z-modifier z-c">static</span> <span class="z-storage z-type z-c">char</span> buf<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">16</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-punctuation z-terminator z-c">;</span> <span class="z-comment z-block z-c"><span class="z-punctuation z-definition z-comment z-c">/*</span> ...HH:MM:SS.MMM <span class="z-punctuation z-definition z-comment z-c">*/</span></span> + <span class="z-support z-type z-stdint z-c">uint32_t</span> now <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">k_uptime_get_32</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">unsigned</span> <span class="z-storage z-type z-c">int</span> ms <span class="z-keyword z-operator z-assignment z-c">=</span> now <span class="z-keyword z-operator z-arithmetic z-c">%</span> MSEC_PER_SEC<span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">unsigned</span> <span class="z-storage z-type z-c">int</span> s<span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">unsigned</span> <span class="z-storage z-type z-c">int</span> min<span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">unsigned</span> <span class="z-storage z-type z-c">int</span> h<span class="z-punctuation z-terminator z-c">;</span> + + now <span class="z-keyword z-operator z-assignment z-augmented z-c">/=</span> MSEC_PER_SEC<span class="z-punctuation z-terminator z-c">;</span> + s <span class="z-keyword z-operator z-assignment z-c">=</span> now <span class="z-keyword z-operator z-arithmetic z-c">%</span> <span class="z-constant z-numeric z-integer z-decimal z-c">60<span class="z-storage z-type z-numeric z-c">U</span></span><span class="z-punctuation z-terminator z-c">;</span> + now <span class="z-keyword z-operator z-assignment z-augmented z-c">/=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">60<span class="z-storage z-type z-numeric z-c">U</span></span><span class="z-punctuation z-terminator z-c">;</span> + min <span class="z-keyword z-operator z-assignment z-c">=</span> now <span class="z-keyword z-operator z-arithmetic z-c">%</span> <span class="z-constant z-numeric z-integer z-decimal z-c">60<span class="z-storage z-type z-numeric z-c">U</span></span><span class="z-punctuation z-terminator z-c">;</span> + now <span class="z-keyword z-operator z-assignment z-augmented z-c">/=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">60<span class="z-storage z-type z-numeric z-c">U</span></span><span class="z-punctuation z-terminator z-c">;</span> + h <span class="z-keyword z-operator z-assignment z-c">=</span> now<span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-meta z-function-call z-c"><span class="z-support z-function z-C99 z-c">snprintf</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">buf<span class="z-punctuation z-separator z-c">,</span> <span class="z-keyword z-operator z-word z-c">sizeof</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span><span class="z-meta z-group z-c">buf</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span><span class="z-punctuation z-separator z-c">,</span> <span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span><span class="z-constant z-other z-placeholder z-c">%u</span>:<span class="z-constant z-other z-placeholder z-c">%02u</span>:<span class="z-constant z-other z-placeholder z-c">%02u</span>.<span class="z-constant z-other z-placeholder z-c">%03u</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-punctuation z-separator z-c">,</span> + h<span class="z-punctuation z-separator z-c">,</span> min<span class="z-punctuation z-separator z-c">,</span> s<span class="z-punctuation z-separator z-c">,</span> ms</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-flow z-return z-c">return</span> buf<span class="z-punctuation z-terminator z-c">;</span> +</span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-end z-c">}</span></span></span> + +<span class="z-storage z-modifier z-c">static</span> <span class="z-storage z-type z-c">int</span> <span class="z-meta z-function z-c"><span class="z-entity z-name z-function z-c">process_mpu6050</span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-storage z-modifier z-c">const</span> <span class="z-storage z-type z-c">struct</span> device <span class="z-keyword z-operator z-c">*</span><span class="z-variable z-parameter z-c">dev</span><span class="z-punctuation z-separator z-c">,</span> <span class="z-storage z-modifier z-c">const</span> <span class="z-storage z-type z-c">struct</span> device <span class="z-keyword z-operator z-c">*</span><span class="z-variable z-parameter z-c">ddev</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-meta z-function z-c"> +</span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span></span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"> + <span class="z-storage z-type z-c">struct</span> sensor_value temperature<span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">struct</span> sensor_value accel<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">3</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">struct</span> sensor_value gyro<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">3</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-type z-c">int</span> rc <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_sample_fetch</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>rc <span class="z-keyword z-operator z-comparison z-c">==</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + rc <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_channel_get</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev<span class="z-punctuation z-separator z-c">,</span> SENSOR_CHAN_ACCEL_XYZ<span class="z-punctuation z-separator z-c">,</span> + accel</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>rc <span class="z-keyword z-operator z-comparison z-c">==</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + rc <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_channel_get</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev<span class="z-punctuation z-separator z-c">,</span> SENSOR_CHAN_GYRO_XYZ<span class="z-punctuation z-separator z-c">,</span> + gyro</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>rc <span class="z-keyword z-operator z-comparison z-c">==</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + rc <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_channel_get</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev<span class="z-punctuation z-separator z-c">,</span> SENSOR_CHAN_AMBIENT_TEMP<span class="z-punctuation z-separator z-c">,</span> + <span class="z-keyword z-operator z-c">&amp;</span>temperature</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>rc <span class="z-keyword z-operator z-comparison z-c">==</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> +<span class="z-comment z-block z-c"><span class="z-punctuation z-definition z-comment z-c">/*</span> printf(&quot;[%s]:%g Cel\n&quot; + &quot; accel %f %f %f m/s/s\n&quot; + &quot; gyro %f %f %f rad/s\n&quot;, + now_str(), + sensor_value_to_double(&amp;temperature), + sensor_value_to_double(&amp;accel[0]), + sensor_value_to_double(&amp;accel[1]), + sensor_value_to_double(&amp;accel[2]), + sensor_value_to_double(&amp;gyro[0]), + sensor_value_to_double(&amp;gyro[1]), + sensor_value_to_double(&amp;gyro[2]));<span class="z-punctuation z-definition z-comment z-c">*/</span></span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> <span class="z-keyword z-control z-c">else</span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-support z-function z-C99 z-c">printf</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>sample fetch/get failed: <span class="z-constant z-other z-placeholder z-c">%d</span><span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-punctuation z-separator z-c">,</span> rc</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">update_mydisplay</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_value_to_double</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-keyword z-operator z-c">&amp;</span>accel<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-separator z-c">,</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">sensor_value_to_double</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-keyword z-operator z-c">&amp;</span>accel<span class="z-meta z-brackets z-c"><span class="z-punctuation z-section z-brackets z-begin z-c">[</span><span class="z-constant z-numeric z-integer z-decimal z-c">2</span><span class="z-punctuation z-section z-brackets z-end z-c">]</span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-separator z-c">,</span> ddev</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-keyword z-control z-flow z-return z-c">return</span> rc<span class="z-punctuation z-terminator z-c">;</span> +</span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-end z-c">}</span></span></span> + + +<span class="z-storage z-type z-c">int</span> <span class="z-meta z-function z-c"><span class="z-entity z-name z-function z-c">main</span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function z-parameters z-c"><span class="z-meta z-group z-c"><span class="z-storage z-type z-c">void</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-meta z-function z-c"> +</span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span></span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"> + <span class="z-storage z-modifier z-c">const</span> <span class="z-storage z-type z-c">struct</span> device <span class="z-keyword z-operator z-c">*</span><span class="z-storage z-modifier z-c">const</span> mpu6050 <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">DEVICE_DT_GET_ONE</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">invensense_mpu6050</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span><span class="z-keyword z-operator z-arithmetic z-c">!</span><span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">device_is_ready</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">mpu6050</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-support z-function z-C99 z-c">printf</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>Device <span class="z-constant z-other z-placeholder z-c">%s</span> is not ready<span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-punctuation z-separator z-c">,</span> mpu6050<span class="z-punctuation z-accessor z-c">-&gt;</span>name</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-flow z-return z-c">return</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + + <span class="z-storage z-type z-c">int</span> ret<span class="z-punctuation z-terminator z-c">;</span> + <span class="z-storage z-modifier z-c">const</span> <span class="z-storage z-type z-c">struct</span> device <span class="z-keyword z-operator z-c">*</span><span class="z-storage z-modifier z-c">const</span> dev <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">DEVICE_DT_GET_ONE</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">nordic_nrf_led_matrix</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span><span class="z-keyword z-operator z-arithmetic z-c">!</span>dev<span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">printk</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>Display device not ready<span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-flow z-return z-c">return</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">display_get_capabilities</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev<span class="z-punctuation z-separator z-c">,</span> <span class="z-keyword z-operator z-c">&amp;</span>caps</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span><span class="z-keyword z-operator z-arithmetic z-c">!</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>caps<span class="z-punctuation z-accessor z-c">.</span><span class="z-variable z-other z-member z-c">supported_pixel_formats</span> <span class="z-keyword z-operator z-c">&amp;</span> PIXEL_FORMAT_MONO01<span class="z-punctuation z-section z-group z-end z-c">)</span></span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">printk</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>Expected pixel format not supported<span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-flow z-return z-c">return</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + + ret <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">display_set_pixel_format</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">dev<span class="z-punctuation z-separator z-c">,</span> PIXEL_FORMAT_MONO01</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>ret <span class="z-keyword z-operator z-comparison z-c">&lt;</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">printk</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>display_set_pixel_format failed: <span class="z-constant z-other z-placeholder z-c">%u</span>/<span class="z-constant z-other z-placeholder z-c">%d</span><span class="z-constant z-character z-escape z-c">\n</span><span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span><span class="z-punctuation z-separator z-c">,</span> + <span class="z-support z-constant z-c">__LINE__</span><span class="z-punctuation z-separator z-c">,</span> ret</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + + <span class="z-keyword z-control z-c">while</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span><span class="z-keyword z-operator z-arithmetic z-c">!</span><span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">IS_ENABLED</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">CONFIG_MPU6050_TRIGGER</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-storage z-type z-c">int</span> rc <span class="z-keyword z-operator z-assignment z-c">=</span> <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">process_mpu6050</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c">mpu6050<span class="z-punctuation z-separator z-c">,</span> dev</span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + + <span class="z-keyword z-control z-c">if</span> <span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span>rc <span class="z-keyword z-operator z-comparison z-c">!=</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-section z-group z-end z-c">)</span></span> <span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-begin z-c">{</span> + <span class="z-keyword z-control z-flow z-break z-c">break</span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + <span class="z-meta z-function-call z-c"><span class="z-variable z-function z-c">k_msleep</span><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-begin z-c">(</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-constant z-numeric z-integer z-decimal z-c">100</span></span></span><span class="z-meta z-function-call z-c"><span class="z-meta z-group z-c"><span class="z-punctuation z-section z-group z-end z-c">)</span></span></span><span class="z-punctuation z-terminator z-c">;</span> + <span class="z-punctuation z-section z-block z-end z-c">}</span></span> + + <span class="z-comment z-block z-c"><span class="z-punctuation z-definition z-comment z-c">/*</span> triggered runs with its own thread after exit <span class="z-punctuation z-definition z-comment z-c">*/</span></span> + <span class="z-keyword z-control z-flow z-return z-c">return</span> <span class="z-constant z-numeric z-integer z-decimal z-c">0</span><span class="z-punctuation z-terminator z-c">;</span> +</span></span><span class="z-meta z-function z-c"><span class="z-meta z-block z-c"><span class="z-punctuation z-section z-block z-end z-c">}</span></span></span> + +</span></code></pre> +<p><img src="/img/accel_final_effect.gif" alt="animation final effect" /></p> + + + + Micro:Bit IO breakout board + 2023-05-05T00:00:00+00:00 + 2023-05-05T00:00:00+00:00 + + https://ntn888.github.io/blog/micro-bit-breakout/ + <p>As we have seen prevously, we have switched our interest to the Nordic family of chips.</p> +<p>Providing a whole portfolio of low-power wireless microcontrollers, we are spoiled for choice when it comes to IoT centric projects. Given that 99% of embedded projects these days is somehow IoT based, this is the rationale behind going for these chips. Infact RF is in the name for Nordic, think nRF (the name of their mcu range).</p> +<p>In a wide range of Nordic chips our go-to is the nRF52833 controller. I find it has the right balance of cost vs performance. Detailed info of this chip <a rel="nofollow noreferrer" href="https://www.nordicsemi.com/products/nrf52833">here</a>.</p> +<p>Now there are two viable hobbiest boards for this MCU<sup class="footnote-reference"><a href="#1">1</a></sup>.</p> +<ul> +<li>nRF52840 Dongle [Which uses a similar chip only difference being double RAM/ROM]</li> +<li>Micro:Bit v2</li> +</ul> +<p><img src="/img/micro_bit.jpg" alt="bbc micro:bit" /></p> +<p>I choose the Micro:Bit. Here's why.</p> +<p>It packs in a lot of on-board sensors, which can be beneficial to quickly getting up and running; and at a reasonable price overhead. It retails for around $17.</p> +<p>It is also widely available.</p> +<p>Although its initial concept was to aid in kids' programming education; it has found its way into the arsenal of the serious firmware developer. Now isn't that a very familiar story?</p> +<p>But there is one hurdle in using the Micro:Bit in breadboarding prototypes. There is a solution to this. Enter, the <em>T-type breakout board</em>.</p> +<p><img src="/img/micro_bit_breakout.jpg" alt="breakout board pic" /></p> +<p>It can be procured from Aliexpress <a rel="nofollow noreferrer" href="https://www.aliexpress.com/item/1005003217623333.html">here</a>. Here's an excerpt of the board's device tree in Zephyr that conveniently shows the GPIO mapping of the nRF52833 to the marked pins in the breakout board.</p> +<pre class="z-code"><code><span class="z-text z-plain">edge_connector: connector { + compatible = &quot;microbit,edge-connector&quot;; + #gpio-cells = &lt;2&gt;; + gpio-map-mask = &lt;0xffffffff 0xffffffc0&gt;; + gpio-map-pass-thru = &lt;0 0x3f&gt;; + gpio-map = &lt;0 0 &amp;gpio0 2 0&gt;, /* P0 */ + &lt;1 0 &amp;gpio0 3 0&gt;, /* P1 */ + &lt;2 0 &amp;gpio0 4 0&gt;, /* P2 */ + &lt;3 0 &amp;gpio0 31 0&gt;, /* P3 */ + &lt;4 0 &amp;gpio0 28 0&gt;, /* P4 */ + &lt;5 0 &amp;gpio0 14 0&gt;, /* P5 */ + &lt;6 0 &amp;gpio1 5 0&gt;, /* P6 */ + &lt;7 0 &amp;gpio0 11 0&gt;, /* P7 */ + &lt;8 0 &amp;gpio0 10 0&gt;, /* P8 */ + &lt;9 0 &amp;gpio0 9 0&gt;, /* P9 */ + &lt;10 0 &amp;gpio0 30 0&gt;, /* P10 */ + &lt;11 0 &amp;gpio0 23 0&gt;, /* P11 */ + &lt;12 0 &amp;gpio0 12 0&gt;, /* P12 */ + &lt;13 0 &amp;gpio0 17 0&gt;, /* P13 */ + &lt;14 0 &amp;gpio0 1 0&gt;, /* P14 */ + &lt;15 0 &amp;gpio0 13 0&gt;, /* P15 */ + &lt;16 0 &amp;gpio1 2 0&gt;, /* P16 */ + &lt;19 0 &amp;gpio0 26 0&gt;, /* P19 */ + &lt;20 0 &amp;gpio1 0 0&gt;; /* P20 */ +}; +</span></code></pre> +<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup> +<p>For an updated post of a newly available alternative see <a href="https://ntn888.github.io/blog/nrf-clones/">this post</a>.</p> +</div> + + + + 802.15.4 Wireless + 2023-04-22T00:00:00+00:00 + 2023-04-22T00:00:00+00:00 + + https://ntn888.github.io/blog/802-15-4-wireless/ + <p>'802.15.4' is a low power low-data-rate wireless LAN comminications protocol ideally suited for microcontrollers. And only requires roughly 10-20mA of current to operate (on a 3.3V supply). It has been widely adopted in many applications, such as wireless sensor networks, home automation, and industrial control. It is similar to the widely popular zigbee protocol but is an open standard. Which means it is shipped with opensource RTOS's for platforms that are compatible... My main gripe is that it is yet not popular among hobyists who tend to reach for WiFi in place of this low power network even in microcontroller settings...</p> +<p>The Nordic seires of microcontrollers well support this protocol along with BLE. Compared to BLE this is more suited for industrial noisy environments!</p> +<p>The IEEE 802.15.4 standard defines the physical layer (PHY) and medium access control (MAC) layer for LR-WPANs(low-rate wireless personal area networks). The PHY layer specifies the radio transmission characteristics, including the modulation scheme, data rate, and frequency band. The MAC layer provides mechanisms for channel access, data framing, and error control.</p> +<p>The IEEE 802.15.4 standard supports two frequency bands: 2.4 GHz and 868/915 MHz. The 2.4 GHz band is used in most LR-WPAN applications and provides a data rate of 250 kbps. The 868/915 MHz band is used in some applications that require longer range but lower data rates.</p> +<p>The MAC layer of IEEE 802.15.4 uses a beacon-enabled mode and a non-beacon-enabled mode. In the beacon-enabled mode, a coordinator device periodically transmits a beacon frame that contains information about the network. In the non-beacon-enabled mode, devices can transmit data at any time, without waiting for a beacon frame.</p> +<p>IEEE 802.15.4 also supports two types of topologies: star and peer-to-peer. In the star topology, all devices communicate with a central coordinator device. In the peer-to-peer topology, devices can communicate directly with each other.</p> +<p>In future articles we will see this protocol in action with the Micro:bitV2 which is equipped with the Nordic nRF52833.</p> + + + + Selfhosted email delivery with postfix + 2022-12-27T00:00:00+00:00 + 2022-12-27T00:00:00+00:00 + + https://ntn888.github.io/blog/postfix-mail/ + <p>Note that it is assumed that a Gitlab docker instance is setup on a Debian 10/11 machine... This article follows the <a href="https://ntn888.github.io/blog/selfhosted-gitlab/">post</a>.</p> +<p>According to the docs, the official image of Gitlab doesn't include a Mail Transfer Agent (MTA) preinstalled. For our purposes this is the program that routes out the email from our system. So we'll install it on the host system and setup Gitlab to route mail to it.</p> +<p>This is simple enough, but getting our local hosted email service to play nice with Gmail is a different story.</p> +<p>We begin by setting up basic postfix email. Install <code>postfix</code> as per <a rel="nofollow noreferrer" href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-postfix-on-ubuntu-20-04">this</a>. We only need <em>step one</em>.</p> +<p>To test:</p> +<pre class="z-code"><code><span class="z-text z-plain">echo &quot;Subject: test&quot; | sendmail -v mymail@mydomain.com +</span></code></pre> +<p>This will not actually deliver the mail as yet; but atleast it should run without errors!</p> +<p>To allow the docker container to access postfix we make a small change to <code>/etc/postfix/main.cf</code>. Add <code>0.0.0.0/0</code> to <code>mynetworks</code>. This allows any IP to initiate email, as IP address change with every container restarts, we will take this approach.</p> +<p>Remember to restart postfix on every edit to this file by:</p> +<pre class="z-code"><code><span class="z-text z-plain">sudo systemctl reload postfix +</span></code></pre> +<p>Then add this smtp settings in our Gitlab's <code>docker-compose.yml</code>:</p> +<pre class="z-code"><code><span class="z-text z-plain">gitlab_rails[&#39;smtp_enable&#39;] = true +gitlab_rails[&#39;smtp_address&#39;] = &quot;gitlab.example.com&quot; +gitlab_rails[&#39;smtp_port&#39;] = 25 +</span></code></pre> +<p>Note that <code>'smtp_address'</code> will be the FQDN that we set as our host machine domain.</p> +<p>Now to test, execute the following:</p> +<pre class="z-code"><code><span class="z-text z-plain">sudo docker exec -it gitlab-compose-web-1 bash +gitlab-rails console -e production + +Notify.test_email(&#39;mymail@mydomain.com&#39;, &#39;Message Subject&#39;, &#39;Message Body&#39;).deliver_now +</span></code></pre> +<p>In the first command <code>gitlab-compose-web-1</code> is the name of the container. To check your running name run <code>sudo docker ps</code>.</p> +<p>Note this will not yet work if you're using a Gmail addresses read on to fix this...</p> +<h2 id="setting-up-sasl-for-gmail-receipients">Setting up SASL (for Gmail receipients)<a class="zola-anchor" href="#setting-up-sasl-for-gmail-receipients" aria-label="Anchor link for: setting-up-sasl-for-gmail-receipients">#</a></h2> +<p>Next thing to do is to install Dovecot. On debian:</p> +<pre class="z-code"><code><span class="z-text z-plain">sudo apt install dovecot-core dovecot-pop3d dovecot-imapd +</span></code></pre> +<p>Then in <code>/etc/dovecot/conf.d/10-master.conf</code>:</p> +<pre class="z-code"><code><span class="z-text z-plain">service auth { + unix_listener auth-userdb { + mode = 0666 + user = postfix + group = postfix + } + + # Postfix smtp-auth + #unix_listener /var/spool/postfix/private/auth { + # mode = 0666 + #} + + # Auth process is run as this user. + #user = $default_internal_user +} +</span></code></pre> +<p>Don't forget to restart dovecot:</p> +<pre class="z-code"><code><span class="z-text z-plain">sudo systemctl restart dovecot +</span></code></pre> +<p>Then modify postfix to use dovecot ADD <code>/etc/postfix/main.cf</code>:</p> +<pre class="z-code"><code><span class="z-text z-plain">smtpd_sasl_type = dovecot +</span></code></pre> +<p>Don't forget to restart postfix:</p> +<pre class="z-code"><code><span class="z-text z-plain">sudo systemctl reload postfix +</span></code></pre> +<p>Lastly we need to add a TXT record for our domain:</p> +<pre class="z-code"><code><span class="z-text z-plain">v=spf1 ip4:x.x.x.x ~all +</span></code></pre> +<p>where <code>x.x.x.x</code> is the IP of the server.</p> +<p>Now try:</p> +<pre class="z-code"><code><span class="z-text z-plain">echo &quot;Subject: test&quot; | sendmail -v me@gmail.com +</span></code></pre> +<p>Tip: handy commands to view the log messages: +postfix: <code>sudo tail -f /var/log/mail.log</code> +Dovecot: <code>sudo tail -f /var/log/syslog</code></p> + + + + Sphinx Documentation Generator + 2022-12-07T00:00:00+00:00 + 2022-12-07T00:00:00+00:00 + + https://ntn888.github.io/blog/sphinx/ + <p>Today I write about the python documentation generator; Sphinx. Which is based on reStructuredText.</p> +<p>reStructuredText is an alternative to Markdown in styling text fragments using simple relevant syntax. However restructured text (or reST for short) is bit more versatile and in my opinion more featured. It is also extensible using third party plugins! One notable feature is the native ability to handle LaTex; which is great for including Mathematic formulas.</p> +<p>In comparison with Markdown, reST aims to be more readable and less obtrusive. For example link addresses are provided with reference as opposed to being inline as in Markdown. The second method is also possible as I said it being versatile.</p> +<p>I intend to document my ideas about RISC-V lowlevel concepts in Sphinx; I believe this will be an interesting take.</p> +<p>One of the initial difficulty in grasping Sphinx is understanding the toctree for linking the table of contents. Which I will clarify here...</p> +<p>Basically it is a recursive link to the other page as a relative reference from the current page's current directory. Once you have generated the base skeleton project from the <code>sphinx-quickstart</code> command; you just need to append these links to other pages in the toc section in <code>index.rst</code>. Like so:</p> +<p>index.rst:</p> +<pre class="z-code"><code><span class="z-text z-plain">.. toctree:: + :maxdepth: 4 + :caption: Contents: + + fm/welcome +</span></code></pre> +<p>And then in <code>./fm/welcome.rst</code> for example:</p> +<pre class="z-code"><code><span class="z-text z-plain"> +================= +Test welcome page +================= + +This is the first proper page we will write. This should show up as the first page from the TOC! + +.. toctree:: + :maxdepth: 4 + + test-page_1 + test-page_2 +</span></code></pre> +<p>The above welcome page contains reference to <code>test-page_1.rst</code> and <code>test-page_2.rst</code> which we will need to provide inside the <code>./fm/</code> subdir.</p> +<p>As is apparent you can structure your book directory into other subfolders as <code>./ch1/</code> and so on.</p> +<p>The Sphinx documentation can be found <a rel="nofollow noreferrer" href="https://www.sphinx-doc.org/en/master/">here</a> and the reST syntax is <a rel="nofollow noreferrer" href="https://docutils.sourceforge.io/docs/user/rst/quickstart.html">here</a>.</p> +<p>Happy Writing!</p> + + + + Sipeed BL702 board + 2022-12-01T00:00:00+00:00 + 2022-12-01T00:00:00+00:00 + + https://ntn888.github.io/blog/sipeed-bl702/ + <p>A well known Chinese kits maker Sipeed, has <a rel="nofollow noreferrer" href="https://www.aliexpress.us/item/1005005012406688.html">started selling</a> a micro board based on the BL702 chip! With some additional peripherals such as an accelerometer and a pixie LED, the board retails for only 4USD. More information can be found <a rel="nofollow noreferrer" href="https://wiki.sipeed.com/hardware/en/maixzero/sense/maix_zero_sense.html">here</a>. +It even has the 4 debug pins collated together on one side! Good ergonomics!</p> +<p><img src="/img/sipeedM0.jpg" alt="Main board with debugger" /></p> +<p>This release from Sipeed paves way for more exposure and popularity which brings more adoption... And in turn more polished toolset and kits. Looking back into the bl_mcu_sdk after some months, there seems to have been an overhaul on the API. Although I'm against making changes (and breaking user's codebase), hopefully this brings better convenence and features!</p> +<p>Considering that the ideal target application is battery powered uses, the board does come with a port for battery input. But it lacks capability for LiPO charging. But this could be easily fixed with an external module such as <a rel="nofollow noreferrer" href="https://www.aliexpress.com/item/1005003781466639.html">this</a>.</p> +<p>Happy Hacking!</p> + + + + Piracy on a Budget + 2022-11-24T00:00:00+00:00 + 2022-11-24T00:00:00+00:00 + + https://ntn888.github.io/blog/piracy/ + <p>The Problem:</p> +<ul> +<li>getting a server with resources for Plex serving capabilities is expensive</li> +<li>getting a server with TB storage is ineffective / moot, then when you have a local server</li> +<li>running the usual home-based seedbox clogs up your connection by seeding all the time<sup class="footnote-reference"><a href="#1">1</a></sup> (you loose your internet's full potential)</li> +</ul> +<p>The Solution: make a 'smart' remote seedbox (on a cheap VPS) that queue's the torrents according to this remote box's available space and immediately delete the torrent after completing and transferring to the local machine. Then progress to the next pending torrent. The biggest advantage here is that if you do a batch request of torrents (something I do all the time; eg a movie collection) it'll handle progressively without stuffing up the disk space!</p> +<p>Okay that's seems like a lot to digest, but we will do this through rtorrent's scripting capabilities together with remote mount.</p> +<p><img src="/img/newposters.jpg" alt="posters" /></p> +<p>The basic principle is simple. We wil use a remote mount to mount the temporary torrents Download directory to a subdir inside the main media directory. (eg: the <em>torrents</em> directory). Then direct radar to this mounted dir as the torrents download directory. Note that we are installing servarr using docker in our local box. If you need instructions on servarr installation see <a rel="nofollow noreferrer" href="https://wiki.servarr.com/">here</a>.</p> +<p>The most complex bit is the installing services on the remote box. We need to install vsftp and rtorrent. Luckily there are automated scripts to get this done. I suggest using 'swizzin'. Once this is out of the way; configuring vsftp is the hardest part... We will see this first.</p> +<p>vsftp by default runs on the traditional ftp mode, where a separate 'data' connection is made from the server to the client(!) on port 21. We will setup the passive mode; where we don't need to open firewalls on the client (!). Use the following config /etc/vsftpd.conf:</p> +<pre class="z-code"><code><span class="z-text z-plain">listen=YES +listen_ipv6=NO + +user_config_dir=/etc/vsftpd/users +pasv_enable=Yes +pasv_max_port=10100 +pasv_min_port=10090 +pasv_address=192.9.174.154 +local_enable=YES +write_enable=YES +force_dot_files=YES +local_umask=022 +dirmessage_enable=YES +use_localtime=YES +xferlog_enable=YES +#connect_from_port_20=NO +utf8_filesystem=YES +require_ssl_reuse=NO +ssl_enable=NO +pam_service_name=vsftpd +secure_chroot_dir=/var/run/vsftpd/empty + +#ascii_upload_enable=YES +#ascii_download_enable=YES +#ftpd_banner=Welcome to blah FTP service. + +############################################# +#Uncomment these lines to enable FXP support# +############################################# +pasv_promiscuous=YES +port_promiscuous=YES + +################### +#Set a custom port# +################### +#listen_port= + +</span></code></pre> +<p>then in the user config directory named after your system username: /etc/vsftpd/users/ubuntu</p> +<pre class="z-code"><code><span class="z-text z-plain">local_root=/path/to/torrents/dir +dirlist_enable=YES +download_enable=YES +write_enable=YES +</span></code></pre> +<p>First do a simple commandline test to test the connection: <code>pftp my-server-ip</code> +Next do a manual mount with the command to check: <code>curlftpfs -o allow_other ftp-user:ftp-pass@my-ftp-location.local /mnt/my_ftp/</code>. More info on mounting <a rel="nofollow noreferrer" href="https://linuxconfig.org/mount-remote-ftp-directory-host-locally-into-linux-filesystem">here</a></p> +<p>Now that we have the system prepared we just need to tweak the radarr/sonarr settings to reflect our setup.</p> +<p>Now we come to the meat of this article. Swizzin' goes a long way in glueing up the services; which is rutorrent with the download client rtorrent. The rtorrent setup lives in <code>~/.rtorrent.rc</code>.</p> +<p>However this link betweent the client and the rutorrent GUI is done using unix sockets. And works within the local host. But we intend to access rtorrent remotely from our home server. We need to setup a reverse proxy using the already installed nginx server to route this socket to the outside world onto a network port.</p> +<p>To do this insert the following block inside of the preexisting 'http' block:</p> +<pre class="z-code"><code><span class="z-text z-plain"># rtorrent XMLRPC + server { + listen 0.0.0.0:8080; + error_log /var/log/nginx/nginx.error.log; + access_log /var/log/nginx/access.log; + + auth_basic &quot;Restricted&quot;; + # Users must log on with matching credentials + auth_basic_user_file /etc/nginx/.htpasswd; + + location /RPC2 { + include scgi_params; + scgi_pass unix:/var/run/ubuntu/.rtorrent.sock; + } + } +</span></code></pre> +<p>Make sure to check the XMLRPC socket path specified in the <code>.rtorrent.rc</code> file. Dont forget to open the firewall port!</p> +<p>Next as indicated we need to provide the auth users list file <code>/etc/nginx/.htpasswd</code>. This is the credentials we will need to provide in radarr connection settings. To do this apply the following commands<sup class="footnote-reference"><a href="#2">2</a></sup>: +<code>sudo sh -c &quot;echo -n 'sammy:' &gt;&gt; /etc/nginx/.htpasswd&quot;</code> +<code>sudo sh -c &quot;openssl passwd -apr1 &gt;&gt; /etc/nginx/.htpasswd&quot;</code> +and enter a password at the prompts.</p> +<p>Then comeback to radarr settings -&gt; Download Client settings, select rtorrent and fill this:</p> +<pre class="z-code"><code><span class="z-text z-plain">Name: rTorrent +rTorrent host: &lt;your-remote-server-ip&gt; +Port: 8080 +URL Path: RPC2 +Use SSL: OFF +Username: &lt;your username&gt; +Password: &lt;your password&gt; +Add label to torrent: TV (or anything else you desire) +Optional - Downloaded files location: &lt;custom directory&gt; +</span></code></pre> +<p>The final thing remaining to complete our connection is to map the remote folder as explained <a rel="nofollow noreferrer" href="https://trash-guides.info/Radarr/Radarr-remote-path-mapping/">here</a>. In our case map the set in the <code>.rtorrent.rc</code> as <code>directory.default.set</code> to the ftp mount directory. The import will occur between these folders after rtorrent has completed the download.</p> +<p>That's it; we can now test a torrent download from radarr! The only downside to our setup is that we dont see the progress when importing; ie. downloading from the torrent box to our local server.</p> +<p>Now we need to script up rtorrent so that it behaves according to our constraints above.</p> +<p>Actually swizzin' gives us a great plugin that is installed by default that takes care of this by itself: QUOTA.</p> +<p>This will manage the disk space issue automatically; just point Quota to the right volume you intend to use for downloads folder when installing swizzin'.</p> +<p>All we need to do now is set the queue length in rutorrent and setup auto-deletion in the .rtorrent.rc file when the ratio is reached. Add this to the file<sup class="footnote-reference"><a href="#3">3</a></sup>:</p> +<pre class="z-code"><code><span class="z-text z-plain">ratio.enable= +ratio.min.set=200 +method.set = group.seeding.ratio.command, &quot;d.close= ; d.erase=&quot; +</span></code></pre> +<p>All Done!</p> +<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup> +<p>I refer to power user's with private trackers that require ratio maintaining. +<sup class="footnote-reference"><a href="#2">2</a></sup>: <a rel="nofollow noreferrer" href="https://www.digitalocean.com/community/tutorials/how-to-set-up-password-authentication-with-nginx-on-ubuntu-14-04">https://www.digitalocean.com/community/tutorials/how-to-set-up-password-authentication-with-nginx-on-ubuntu-14-04</a> +<sup class="footnote-reference"><a href="#3">3</a></sup>: <a rel="nofollow noreferrer" href="https://github.com/rakshasa/rtorrent/wiki/RTorrentRatioHandling">https://github.com/rakshasa/rtorrent/wiki/RTorrentRatioHandling</a></p> +</div> + + + + Gitlab Selfhohsted! + 2022-11-23T00:00:00+00:00 + 2022-11-23T00:00:00+00:00 + + https://ntn888.github.io/blog/selfhosted-gitlab/ + <p>Carrying on my spree of selfhosted software, I moved to hosting my on instance of the free and opensource ==Gitlab==. I think this will be the most rewarding selfhosted project given it's useful nature.</p> +<p>To peek on my installed instance goto <a rel="nofollow noreferrer" href="https://gitlab.myjabber.site/ajit">http://gitlab.myjabber.site/</a>.</p> +<p>The good news is that it only requires a cheap less powerful instance. On the flip side we need 2 of those! A dedicated one is recommended for the CI/CD runner application, for acceptable performance. Or it may hurt the responsiveness.</p> +<p>And it was a convenient time to hustle a deal on VPS with black friday on! As being cost conscious as ever; I headed to <a rel="nofollow noreferrer" href="https://lowendbox.com/">Lowendbox</a> to snap a deal. At the time I checked <em>Racknerd</em> had a 2.5G deal for 23.50usd yearly. And then for the Gitlab Runner instance, I opted for the CloudServer's 1G for 10usd/yearly. It takes a lot of time to compile the pipiline on some projects but it'd fine for my usecase...</p> +<p>I initially rushed in for a native binary install on the Debian 11 machine I setup. Then I reset the VM and went for the docker based setup, which is just straightforward. Once this is done, you have to download the binary for the runner onto the secondary VM and set it up to attach to the main instance. You will find instructions to do this on the gitlab's documention website.</p> +<p>For those interested here's a copy of my docker-compose.yml.</p> +<pre class="z-code"><code><span class="z-text z-plain">version: &#39;3.6&#39; +services: + web: + image: &#39;gitlab/gitlab-ee:latest&#39; + restart: always + hostname: &#39;gitlab&#39; + environment: + GITLAB_OMNIBUS_CONFIG: | + external_url &#39;https://gitlab.myjabber.site&#39; + gitlab_rails[&#39;gitlab_shell_ssh_port&#39;] = 2289 + # Add any other gitlab.rb configuration here, each on its own line + pages_external_url &quot;http://pages.myjabber.site&quot; # not a subdomain of external_url + #pages_nginx[&#39;redirect_http_to_https&#39;] = true + gitlab_rails[&#39;smtp_enable&#39;] = true + gitlab_rails[&#39;smtp_address&#39;] = &quot;smtp.mailgun.org&quot; + gitlab_rails[&#39;smtp_port&#39;] = 587 + gitlab_rails[&#39;smtp_authentication&#39;] = &quot;plain&quot; + gitlab_rails[&#39;smtp_enable_starttls_auto&#39;] = true + gitlab_rails[&#39;smtp_user_name&#39;] = &quot;&lt;from address&gt;&quot; + gitlab_rails[&#39;smtp_password&#39;] = &quot;&lt;password_here&gt;&quot; + gitlab_rails[&#39;smtp_domain&#39;] = &quot;&lt;mailgun domain&gt;&quot; + ports: + - &#39;80:80&#39; + - &#39;443:443&#39; + - &#39;2289:22&#39; + volumes: + - &#39;$GITLAB_HOME/config:/etc/gitlab&#39; + - &#39;$GITLAB_HOME/logs:/var/log/gitlab&#39; + - &#39;$GITLAB_HOME/data:/var/opt/gitlab&#39; + shm_size: &#39;256m&#39; +</span></code></pre> +<p>and the .env file in the same folder:</p> +<pre class="z-code"><code><span class="z-text z-plain">GITLAB_HOME=/srv/gitlab +</span></code></pre> +<p><del>You may have noticed the use of smtp settings. This is used to mail the users with notifications. I've used Mailgun as my email forwarder; which I think is the goto these days. You can send 2000 mails in the free tier.</del> The free tier has been stopped! Check <a rel="nofollow noreferrer" href="https://simplycreate.online/update/2022/12/26/postfix_mail.html">this</a> for local email forwarder.</p> +<p>Note: I had trouble setting up Gitlab Pages to work on my instance, this may have been because of SSL certificates improperly installed. But I gave-up on this issue and commented out the lines above relating to Pages. I suppose it's not that important on a self hosted instance!?</p> +<p>Finally don't forget to setup the firewall!!</p> +<ul> +<li>A note on backup: Given the sensitive nature of one's own code repository it is a no brainer to setup backups. You could use the Oracle's free tier compute VPS for this purpose. We will spinup the AMD [] instead of the ARM Ampere since we dont need resources for this purpose.</li> +</ul> +<p>Proceed to install rsync on both machines and follow with the script provided <a rel="nofollow noreferrer" href="https://blog.ssdnodes.com/blog/vps-backups-simple-overthinking/">here</a>.</p> + + + + C linker scripts howto + 2022-11-16T00:00:00+00:00 + 2022-11-16T00:00:00+00:00 + <p>In this article series we will look at dealing and understanding linker script writing. Targetting the RISC-V RV32 core. We begin with looking at programming assembly, as this will help understand the innards of the microcontroller.</p> + + + https://ntn888.github.io/blog/c-linkerscripts/ + <p>In this article series we will look at dealing and understanding linker script writing. Targetting the RISC-V RV32 core. We begin with looking at programming assembly, as this will help understand the innards of the microcontroller.</p> +<span id="continue-reading"></span> +<p>The RISC-V chip was intended and intentionally designed as a heuristic use case, coming in from academic origins has now found popularity among the commercial setting. As such it is ideal microcontroller for tackling to understand the assembly environment, and given it's rising populatity among the other giants in the field especially ARM cortex; has practical value.</p> +<p>You may ofcourse run a simulation by installing the toolchain and qemu along with gdb; which then enables the ability to run in step and inspect the memory state. However for a total beginner an all inclusive ready-built package is more useful. <a rel="nofollow noreferrer" href="https://github.com/mortbopet/Ripes">Ripes</a> is such a package. It comes as an appimage so just download and execute from the terminal to fire it up!</p> +<p>One major advantage of Ripes is that it contains simulation of external peripherals such as LED matrix and switches. Which makes it more engaging for the new-commer.</p> +<p><a href="/img/ripes_animation.gif">Ripes in action</a></p> +<p>Stay tuned..</p> + + + + DOOM Emacs intro + 2022-11-16T00:00:00+00:00 + 2022-11-16T00:00:00+00:00 + + https://ntn888.github.io/blog/doom/ + <p>I've always been shuffling around <code>neovim</code>, <code>emacs</code>, <code>vscode</code>. This writeup will explain how I've settled on the use of emacs. DOOM Emacs specifically.</p> +<p><img src="/img/yay_evil_by_ultravioletbat_d1uicfh.jpg" alt="doom_logo" /></p> +<p>It's github page is accessible <a rel="nofollow noreferrer" href="https://github.com/doomemacs/doomemacs">DOOM Emacs</a>; documentation <a rel="nofollow noreferrer" href="https://github.com/doomemacs/doomemacs/blob/master/docs/index.org">here</a> and installation for all platform are accessible <a rel="nofollow noreferrer" href="https://github.com/doomemacs/doomemacs/blob/master/docs/getting_started.org">here</a>.</p> +<p>DOOM Emacs is a batteries included edition of emacs. Together with which-key and easy language enable features (layers), it has a very intuitive experience which is ideal for the beginner (to command line editior altogether!) I think DOOM's got a clean modern funky interface too.</p> +<p>The one thing it's lacking is a newbie friendly guide/intro to using it. In contrast to my opinion; the developers' hold that DOOM is for an experienced emacs user. I beg to differ as stated above.</p> +<p>To help with this user onboarding issue, when I've tried to pick up usage, I've scoured the internet to put together a single sheet<sup class="footnote-reference"><a href="#1">1</a></sup> summarising all the essential commands. Accessible <a href="/img/doom-cmds.pdf">here</a>.</p> +<p>Let me know down what's you favourite newbie friendly emacs edition is?</p> +<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup> +<p>That's all the familiarity it requires. That's how intuitive it is!</p> +</div> + + + + Self hosted Mastodon Instance + 2022-11-13T00:00:00+00:00 + 2022-11-13T00:00:00+00:00 + + https://ntn888.github.io/blog/mastodon/ + <p>In keeping with the times, I messed about with setting up an own Mastodon self-hosted instance!</p> +<p><del>An ideal VM is the Oracle's OCI cloud service. In there they provide a 4vcore, 24GB ram(!) as a free service. Which is plenty for this purpose. In contrast to the other competitors offering which I found is actually insufficient for self hosting Mastodon. Additionally you get 150G block storage free (in addition to 50G boot volume). Oracle calls it &quot;VM.Standard.A1.Flex&quot;.</del></p> +<p>I instead went ahead with LUXVPS. They allow open port on 25 (upon request) for sending emails from local machine without a middleman subscription (and they get expensive quick). They're prices are also very affordable; for 3EUR you get a 1c / 4GB VM with KVM virtualisation. In my experiment this is sufficient... My instance with two test users, with federation relay and full text search only consumes 1G Ram and plenty of processor left. Should be enough for a small gathering :).</p> +<hr /> +<p>If you're taking the local server option for emails, you have a good guide here: <a rel="nofollow noreferrer" href="https://doc.dovecot.org/configuration_manual/howto/postfix_and_dovecot_sasl/">https://doc.dovecot.org/configuration_manual/howto/postfix_and_dovecot_sasl/</a>. Setting up your own local mail server is tricky with a combination of packages required. This is due to the fact most mail providers (especially Google's GMail) require SASL authentication. But it's worth the effort I think. Again one thing you cannot avoid is that your setup will send the mail almost invariably to the users SPAM folder! But you can always opt for a mail forwarder subscription.</p> +<p>Following on from the above link, some lines has to be commented in /etc/postfix/master.cf:</p> +<pre class="z-code"><code><span class="z-text z-plain">submission inet n - n - - smtpd + -o smtpd_tls_security_level=encrypt + -o smtpd_sasl_auth_enable=yes + -o smtpd_sasl_type=dovecot + -o smtpd_sasl_path=private/auth + -o smtpd_sasl_security_options=noanonymous + -o smtpd_sasl_local_domain=$myhostname +# -o smtpd_client_restrictions=permit_sasl_authenticated,reject +# -o smtpd_sender_login_maps=hash:/etc/postfix/virtual + -o smtpd_sender_restrictions=reject_sender_login_mismatch +# -o smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject +</span></code></pre> +<p>Here's the mastodon config (/home/mastodon/live/.env.production)relating to smtp:</p> +<pre class="z-code"><code><span class="z-text z-plain">SMTP_SERVER=localhost +SMTP_PORT=587 +SMTP_LOGIN= +SMTP_PASSWORD= +SMTP_FROM_ADDRESS=notifications@yourdomain.online +SMTP_AUTH_METHOD=plain +SMTP_OPENSSL_VERIFY_MODE=none +SMTP_ENABLE_STARTTLS_AUTO=true +</span></code></pre> +<hr /> +<p>Once you get a VM instance up and running, continue with the official help page: <a rel="nofollow noreferrer" href="https://docs.joinmastodon.org/admin/install/">https://docs.joinmastodon.org/admin/install/</a>. I installed on a Debian 11 image.</p> +<p>An 'errata' to the last bit with certbot has input here if you run into problems: <a rel="nofollow noreferrer" href="https://github.com/mastodon/documentation/issues/940">https://github.com/mastodon/documentation/issues/940</a>. +Don't forget the prerequisites: <a rel="nofollow noreferrer" href="https://docs.joinmastodon.org/admin/prerequisites/">https://docs.joinmastodon.org/admin/prerequisites/</a></p> +<p>My instance setup is accessible via <a rel="nofollow noreferrer" href="https://simplysocial.online/">https://simplysocial.online/</a><sup class="footnote-reference"><a href="#1">1</a></sup>. Users can create accounts on this instance!</p> +<p>Stay in touch for further discussion on integrating cloudflare with Backblaze for free egress, in bringing costs down further!</p> +<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup> +<p>In Ubuntu 22.04 you need to run <code>sudo usermod -a -G mastodon www-data</code> to fix permissions issue: https://github.com/mastodon/mastodon/discussions/19651</p> +</div> + + + + Review: PI Pico 4MB version + 2022-11-04T00:00:00+00:00 + 2022-11-04T00:00:00+00:00 + + https://ntn888.github.io/blog/pico-4m/ + <p><img src="/img/4MB_Pico.jpg" alt="Pico 4M versio" /></p> +<p>I recently tried out the PICO 4MB version sold here: <a rel="nofollow noreferrer" href="https://www.aliexpress.com/item/1005003928558306">https://www.aliexpress.com/item/1005003928558306</a>. A cheaper alternative to the official version but with the larger flash.</p> +<p>Specifically called &quot;Lite Black&quot; by the seller, it has a similar layout to the original with the single LED at port-25.</p> +<p>Considering it's an Cortex-M chipset (M0+) it will be programmed by a ST-LINK (SWD) debugger probe. The PI Pico comes with it's own programming environment / SDK for building application in C/C++.</p> +<p>This 4MB variant at it's given price is an excellent alternative to the classic bluepill which lacks flash for any complex applications. And will be a drop in replacement for any bluepill suitable projects in future.</p> +<p>Articles about Pico Projects soon to come...</p> + + + + Switching to the nRFMicro + 2022-03-20T00:00:00+00:00 + 2022-03-20T00:00:00+00:00 + + https://ntn888.github.io/blog/nrfmicro/ + <p>In response to my <a href="https://ntn888.github.io/blog/thoughts-esp-idf/">rants</a> on the low-power wireless situation with the ESP ecosystem, I've finally (hopefully :-) ) decided instead to base on the nRF52 platform using Zephyr RTOS. These chips come with onboard low-power wireless radio including the 802.15.4; ideal for short distance networking.</p> +<p>My biggest gripe was that the ecosystem didn't support hobbyiest level cheap development modules. This is not the case anymore since I found this breakout board project on <a rel="nofollow noreferrer" href="https://github.com/joric/nrfmicro">github</a>. This board can be ordered on <a rel="nofollow noreferrer" href="https://www.pcbway.com/project/shareproject/nRFMicro_1_4.html">PCBWay</a> for like $30 for an assembled 5 bunch.</p> +<p>The board lacks out-of-the box support from Zephyr, but this could easily be solved by an upstream pull-request. Also featuring the board on the Zephyr supported list could bring in some more exposure; I had to ravage through the internet to come across this project!</p> + + + + Thoughts on the ESP-IDF SDK + 2022-03-19T00:00:00+00:00 + 2022-03-19T00:00:00+00:00 + + https://ntn888.github.io/blog/thoughts-esp-idf/ + <p>By contrast the esp-idf SDK feels intuitive and very matured. This is a given, considering the popularity of the ESP chips. My only gripe about this ecosystem is the lack of lowpower wireless module. Although this is being fulfilled with the advent of the upcoming ESP32-H2, it's a long overdue wait!</p> +<blockquote> +<p>Even though the existing chips feature a BLE radio; it's not as power efficient as it should be, negating any possibility of deploying into low-power applications.</p> +</blockquote> +<p>I did consider the well know nRF52840 chip targetting low power wireless applications. A huge advantage here is that it's already available! But most dev-boards, including simple ones like Adafruit Feather and Arduino Nano 33 BLE go for a whopping 40-50usd! Even the company's own (hideous looking) nRF52 dongle is 15usd. Which in my view makes it inaccessible for hobbyist applications.</p> +<p>In the end, this allows me to trial out the esp-idf SDK with the on-hand chip: esp32-c3, while waiting for the H2.</p> +<p>I personally think this is a shameful situation in the maker world in this day and age. Or I might be missing something.</p> + + + + Switching to the ESP32-C3 + 2022-03-18T00:00:00+00:00 + 2022-03-18T00:00:00+00:00 + + https://ntn888.github.io/blog/esp32c3/ + <p>This blog will here-on switch to the esp32-c3 controller...</p> +<p>It is noted that the esp32 chips are infamous for higher power consumption, especially with the radio on. But it's expected that this problem will disappear with the introduction of the upcomming ESP32-H2 MCU.</p> +<p>The rationale for the switch is the incomplete and nonfunctional tooling on the Buffalo Lab boards, especially the inability to use JTAG debugging.</p> +<p>The esp32-c3 is a similarly low cost solution with a mature SDK and built-in debugger.</p> + + + + Debugging BL702 + 2022-03-17T00:00:00+00:00 + 2022-03-17T00:00:00+00:00 + + https://ntn888.github.io/blog/debug-bl702/ + <p>You may use the advertised <a rel="nofollow noreferrer" href="https://www.cnx-software.com/2021/06/21/rv-debugger-plus-uart-jtag-debug-board-bl702-zigbee-ble-risc-v-soc/">Sipeed RV-Debugger Plus</a>. It emulates a dual device one for the flashing (UART) and for JTAG.</p> +<p>Then hookup the probe according to the following pinouts on the BL702:</p> +<table><thead><tr><th>BL702 Pin</th><th>JTAG Function</th></tr></thead><tbody> +<tr><td>D0</td><td>TMS</td></tr> +<tr><td>D1</td><td>TDI</td></tr> +<tr><td>D2</td><td>TCK</td></tr> +<tr><td>D9</td><td>TDO</td></tr> +</tbody></table> +<p>You may ofcourse simply plug it with breadboard dupont wires. But I found it convenient to use one of these 2x5 IDC ribbon cables:</p> +<p><img src="/img/idc_ribbon.jpg" alt="IDC ribbon cable" /></p> +<p>and solder them on straight to the board like this:</p> +<p><img src="/img/soldered_board.jpg" alt="soldered board" /></p> +<p>Once the hardware is setup, place the following files in the <code>~/openocd-cfg/</code> directory:</p> +<p>In <code>if_rv_dbg_plus.cfg</code>:</p> +<pre class="z-code"><code><span class="z-text z-plain"># BouffaloLab USB-JTAG/TTL adapter +adapter driver ftdi +ftdi_vid_pid 0x0403 0x6010 + +ftdi_channel 0 +#ftdi_tdo_sample_edge falling +transport select jtag +adapter speed 4000 + +ftdi_layout_init 0x00f8 0x00fb +#ftdi_layout_signal nTRST -data 0x0400 +#ftdi_layout_signal nSRST -ndata 0x0020 + +#reset_config srst_only srst_push_pull +#adapter_nsrst_delay 100 +#adapter_nsrst_assert_width 100 +</span></code></pre> +<p>In <code>tgt_702.cfg</code>:</p> +<pre class="z-code"><code><span class="z-text z-plain">#target chip + +set _CHIPNAME riscv +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x20000e05 + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME +$_TARGETNAME.0 configure -work-area-phys 0x22020000 -work-area-size 0x10000 -work-area-backup 1 +#$_TARGETNAME.0 configure -rtos auto + +echo &quot;Ready for Remote Connections&quot; + +$_TARGETNAME.0 configure -event reset-assert-pre { + echo &quot;reset-assert-pre&quot; + adapter speed 100 +} + +$_TARGETNAME.0 configure -event reset-deassert-post { + echo &quot;reset-deassert-post&quot; + adapter speed 4000 + reg mstatus 0x7800 + reg mie 0x0 +# reg pc 0x22008000 +} + +$_TARGETNAME.0 configure -event reset-init { + echo &quot;reset-init&quot; +# 4MHz for FPGA + adapter speed 4000 +} + +gdb_memory_map enable +gdb_flash_program disable + +riscv set_prefer_sba on +riscv set_command_timeout_sec 1 + +init +reset init + +#jtag arp_init + +#resume +#exit +</span></code></pre> +<p>In <code>702.init</code>:</p> +<pre class="z-code"><code><span class="z-text z-plain">set architecture riscv:rv32 +#target remote :3333 +#set disassemble-next-line on +set mem inaccessible-by-default off +#gdb_breakpoint_override [hard|soft|disable] + +mem 0x22008000 0x22014000 rw +mem 0x42008000 0x42014000 rw +mem 0x22014000 0x22020000 rw +mem 0x42014000 0x42020000 rw +mem 0x22020000 0x22030000 rw +mem 0x42020000 0x42030000 rw +mem 0x22030000 0x2204C000 rw +mem 0x42030000 0x4204C000 rw +mem 0x23000000 0x23400000 ro +</span></code></pre> +<p>Then run:</p> +<pre class="z-code"><code><span class="z-text z-plain">openocd -f ~/openocd-cfg/if_rv_dbg_plus.cfg -f ~/openocd-cfg/tgt_702.cfg +</span></code></pre> +<p>Hopefully you'll get the following message:</p> +<pre class="z-code"><code><span class="z-text z-plain">[riscv.cpu.0] Target successfully examined. +</span></code></pre> +<p>Now that openocd sucessfully connects to the target board, next step is to fire-up gdb:</p> +<pre class="z-code"><code><span class="z-text z-plain">~/bl_iot_sdk/toolchain/riscv/Linux/bin/riscv64-unknown-elf-gdb &lt;path_to_project&gt;/build_out/main.elf -x ~/openocd-cfg/702.init +</span></code></pre> +<p>Now you can finally attach a GDB session like this on the <code>(gdb)</code> prompt:</p> +<pre class="z-code"><code><span class="z-text z-plain">target extended-remote localhost:3333 +</span></code></pre> +<p>Further, considering that the dev board uses external flash, there are two ways to debug on chip:</p> +<ul> +<li>debug code on ram</li> +<li>pre-load code to xip as usual using serial, before starting the debug session.</li> +</ul> +<p>More details can be found on the official pdf<sup class="footnote-reference"><a href="#1">1</a></sup>. We will use the second method, on the (gdb) prompt:</p> +<pre class="z-code"><code><span class="z-text z-plain">set $pc = 0x21000000 +set $mie = 0 +set $mstatus = 0x1880 +</span></code></pre> +<p>Now you can step throught the code and probe to your heart's liking. For example:</p> +<pre class="z-code"><code><span class="z-text z-plain">thb main +continue +continue +</span></code></pre> +<p>See more commands in the official pdf<sup class="footnote-reference"><a href="#1">1</a></sup>. To learn more about GDB see <a rel="nofollow noreferrer" href="https://www.cprogramming.com/gdb.html">here</a>.</p> +<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup> +<p>https://github.com/bouffalolab/bl_docs/blob/main/BL602_Openocd%26GDB/en/Introduction%20of%20OpenOCD%20and%20GDB.pdf</p> +</div> +<blockquote> +<p>You can also use a typical ftdi232rl as a debugger probe for openocd as dicussed in <a href="https://ntn888.github.io/blog/diy-jtag-debugger/">this post</a>. But due to conflicts on pins for UART and JTAG mode on the ftdi module, you'll need to setup two modules each for flashing (UART) and probing (JTAG).</p> +</blockquote> + + + + SSD1306 OLED Display + 2022-03-17T00:00:00+00:00 + 2022-03-17T00:00:00+00:00 + + https://ntn888.github.io/blog/ssd1306/ + <p>The SSD1306 128x64 oled display is a popular I/O device that can be added to any project for example to provide some visual diagnostics. It comes in two main variaties; the I2C and the SPI version. We'll be using the I2C one.</p> +<p><img src="/img/oled.jpg" alt="SSD1306 128x64" /></p> +<p>Fortunately the SDK has already the implementation for the lvgl graphics library (provides APIs to simplify access to the display), so we just need to write the I2C protocol driver for oled. </p> + + + + Low-cost microcontrollers for hobbyists + 2022-03-16T00:00:00+00:00 + 2022-03-16T00:00:00+00:00 + + https://ntn888.github.io/blog/mcu-selection-guide/ + <p>There are a multitude of chip manufacturers, let alone individual chip modules. It can get overwhelming quickly for a newbie in the field of embedded or IOT systems, especially if all you want to do is tinker at a hobbyist level.</p> +<p>I present to you a selection guide based on 3 modules to serve a wide range of applications. The rationale being, by having only a few select chips in your toolset you can put the effort to master your chips and get into the nitty gritty. This leads to less frustration when developing and also by working on the native toolsets for the chip in hand, you're confident in producing the most efficient code.</p> +<p>With this in mind I present to you the following list<sup class="footnote-reference"><a href="#2">1</a></sup>:</p> +<ul> +<li> +<p><del>Bluepill (stm32f103c8)</del><sup class="footnote-reference"><a href="#1">2</a></sup>: sub-dollar chip that's simple and very compact formfactor. Deploy it for the simplest of applications. You can still run freeRTOS! And even drive an OLED display! An excellent intro using free licence tools are covered in the book: <a rel="nofollow noreferrer" href="https://link.springer.com/book/10.1007/978-1-4842-3624-6">Beginning STM32</a> by Warren Gay.</p> +</li> +<li> +<p>Micro:Bit (nRF52833): low power wireless! When it comes to low power wireless, Nordic is the undisputed king. RF is in their name and they have gained good popularity in the industry over the past years. See <a href="https://ntn888.github.io/blog/micro-bit-breakout/">this post</a> for viable hobbyist boards.</p> +</li> +<li> +<p>XT-ZB1 (bl702): the king of versatility! Although intended for IOT applications, the $2 price tag opens the possibilities to deploy into anything! A RISC-V clocked at 144 MHz including FPU. That’s decent enough for most of demanding applications. Note that the fact the SDK is still in developement (after 2 years of release!) and that it's unsupported by any IOT RTOS frameworks (RT-Thread for example) makes is a questionable choice right now and we go with the above chip for wireless.</p> +</li> +</ul> +<p>Between the Bluepill and the nRF52, I believe you have the world of embedded at your hands!</p> +<div class="footnote-definition" id="2"><sup class="footnote-definition-label">1</sup> +<p>If you're interested in beginner boards or controller see <a href="https://ntn888.github.io/blog/begin-embedded/">this post</a>.</p> +</div> +<div class="footnote-definition" id="1"><sup class="footnote-definition-label">2</sup> +<p>Edit: See <a href="https://ntn888.github.io/blog/pico-4m/">this post</a> about the PI Pico variant. This would be an excellent replacement for the bluepill. With 4M flash and 130MHz M0+ processor it is a no brainer for new projects!!</p> +</div> + + + + Board Setup Config + 2022-03-15T00:00:00+00:00 + 2022-03-15T00:00:00+00:00 + + https://ntn888.github.io/blog/bsp-config/ + <p>The board setup configuration is included in the path <code>components/platform/soc/bl702/bl702_std/BSP_Board</code>. In our case it's the <code>bl702_evb</code> sub directory. It houses three files:</p> +<ul> +<li><code>clock_config.h</code></li> +<li><code>peripheral_config.h</code> </li> +<li><code>pinmux_config.h</code> </li> +</ul> +<p>The <code>clock_config.h</code> can be used as-is for our board XT-ZB1 without any changes.</p> +<p>The complimentary online <a rel="nofollow noreferrer" href="https://dev.bouffalolab.com/media/config/index.html">GUI Tool</a> is handy for new users to intuitively generate these config files according to desired custom configurations.</p> +<p>&lt; TODO &gt;</p> +<p><em>Find out:</em></p> +<ul> +<li>how to over-ride these files locally per project</li> +<li>need to manually edit the <code>pinmux_config.h</code> to accomodate an enabled peripheral (eg SPI)?</li> +</ul> + + + + Analog Filters Primer + 2022-02-27T00:00:00+00:00 + 2022-02-27T00:00:00+00:00 + + https://ntn888.github.io/blog/analog-filters-primer/ + <p>Noise and interference is inherent in any electrical/electronic system. Their effects are especially prominent in analog systems. Most if not all embedded systems are connected to these analog signal lines and fall prey to this problem. Increasingly we find ready made modules that have digitised interface to the analog sensor. But there are times when we struck upon a novel problem and have to deal it ourselves.</p> +<p>Among other things analog filters help mitigate this problem. It is very often the case that this falls in scope of the task of an embedded/firmware developer due to being fairly connected.</p> +<p>Here we see the available resources to help build know-how of analog filtering. The following is a list of copy-left books and resources in sequential progressive order:</p> +<p><a rel="nofollow noreferrer" href="https://open.umn.edu/opentextbooks/textbooks/dc-electrical-circuit-analysis-a-practical-approach-fiore">DC Electrical Circuit Analysis</a></p> +<p><a rel="nofollow noreferrer" href="https://open.umn.edu/opentextbooks/textbooks/883">AC Electrical Circuit Analysis</a></p> +<p><a rel="nofollow noreferrer" href="https://open.umn.edu/opentextbooks/textbooks/semiconductor-devices-theory-and-application">Semiconductor Devices</a></p> +<p><a rel="nofollow noreferrer" href="https://open.umn.edu/opentextbooks/textbooks/operational-amplifiers-linear-integrated-circuits-theory-and-application-3e">Operational Amplifiers &amp; Linear Integrated Circuits</a></p> +<blockquote> +<p>You may use simulation on PC to experiment and follow along with the above texts. Kicad has a simulation mode which can be used after entering the schematic in eeschema. Look here: <a rel="nofollow noreferrer" href="http://ngspice.sourceforge.net/ngspice-eeschema.html">http://ngspice.sourceforge.net/ngspice-eeschema.html</a></p> +</blockquote> +<p>Also a prerequisite to above circuit analysis is some basic highschool math: calculus and complex numbers. Refer:</p> +<p><a rel="nofollow noreferrer" href="https://open.umn.edu/opentextbooks/textbooks/415">Calculus: Early Transcendentals</a></p> +<p><a rel="nofollow noreferrer" href="https://open.umn.edu/opentextbooks/textbooks/a-first-course-in-electrical-and-computer-engineering">A First Course in Electrical and Computer Engineering</a></p> + + + + Octave Command Line Mode + 2022-02-27T00:00:00+00:00 + 2022-02-27T00:00:00+00:00 + + https://ntn888.github.io/blog/octave-cli/ + <p>GNU Octave has a lesser known command line mode. I think it's awesome. You get to work in the always familiar environment in the shell with vim running alongside to edit script files... You have access to certain shell commands such as <code>cd</code>, <code>ls</code>, <code>mkdir</code>. Enough to navigate you way around. Makes you feel like a true hacker rather than working in the outdated, ugly GUI IDE. +Here's a snapshot:</p> +<pre class="z-code"><code><span class="z-text z-plain">&gt; octave +GNU Octave, version 6.1.1~hg.2021.01.26 +Copyright (C) 2020 The Octave Project Developers. +This is free software; see the source code for copying conditions. +There is ABSOLUTELY NO WARRANTY; not even for MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. For details, type &#39;warranty&#39;. + +Octave was configured for &quot;x86_64-pc-linux-gnu&quot;. + +Additional information about Octave is available at https://www.octave.org. + +Please contribute if you find this software useful. +For more information, visit https://www.octave.org/get-involved.html + +Read https://www.octave.org/bugs.html to learn how to submit bug reports. +For information about changes from previous versions, type &#39;news&#39;. + +octave:1&gt; pi +ans = 3.1416 +octave:2&gt; +</span></code></pre> + + + + Device Drivers 101 + 2022-02-24T00:00:00+00:00 + 2022-02-24T00:00:00+00:00 + + https://ntn888.github.io/blog/device-drivers-101/ + <blockquote> +<p>The SDK has the provisions for <a rel="nofollow noreferrer" href="https://elinux.org/Device_Tree_Reference">device tree system</a>. However this has been segregated into the flash tool with all the communication ports enabled. We will write our device driver in the conventional way in pure C, built on top of the relevant peripheral API; eg, UART, I2C...</p> +</blockquote> +<p><img src="/img/PCF8574.jpeg" alt="i2c gpio extender" /></p> +<h2 id="i2c-gpio-extender">I2C GPIO extender<a class="zola-anchor" href="#i2c-gpio-extender" aria-label="Anchor link for: i2c-gpio-extender">#</a></h2> +<p>To exercise the I2C bus in this post, we’ll be using the PCF8574 GPIO extender chip. This is a great chip for adding additional GPIO lines, provided that you don’t need high speed (the demo operates the I2C bus at ??? kHz). This will be an exercise in writing device drivers; first of many more to come!</p> + + + + freeRTOS Overview + 2022-02-24T00:00:00+00:00 + 2022-02-24T00:00:00+00:00 + + https://ntn888.github.io/blog/freertos/ + <p>The SDK includes the freeRTOS as the system kernel. More info can be seen by clicking on the 'More Info' button on the top of this page. But the kernel is already configured for use in the SDK, so we do not need to worry about it's config or setting up.</p> +<p>In this article we'll go through a rough overview of freeRTOS of just the necessary features.</p> +<p><img src="/img/2tasks.gif" alt="2tasks" /></p> +<h2 id="tasks">Tasks<a class="zola-anchor" href="#tasks" aria-label="Anchor link for: tasks">#</a></h2> +<pre class="z-code"><code><span class="z-text z-plain"> +#include &lt;hosal_gpio.h&gt; +#include &lt;FreeRTOS.h&gt; +#include &lt;task.h&gt; + +static hosal_gpio_dev_t gp1, gp2; + +void my_task (void *p) +{ + static _Bool val = 1; + + while (1) { + hosal_gpio_output_set(&amp;gp2, val); + val = !val; + vTaskDelay(pdMS_TO_TICKS(700)); + } +} + +void main (void) +{ + // setup + gp1.port = 5; + gp1.config = OUTPUT_OPEN_DRAIN_NO_PULL; + hosal_gpio_init(&amp;gp1); + _Bool value = 1; + // setup second gpio + gp2.port = 4; + gp2.config = OUTPUT_OPEN_DRAIN_NO_PULL; + hosal_gpio_init(&amp;gp2); + + xTaskCreate(my_task, &quot;second_entry&quot;, 1024, NULL, 15, NULL); + + while (1) { // should never reach this point + hosal_gpio_output_set(&amp;gp1, value ); + value = !value; + vTaskDelay(pdMS_TO_TICKS(500)); + } +} +</span></code></pre> +<p>Tasks are created using the function in line 30. You need to specify the entry callback function here (as first parameter) and have it defined. The external task we define here is <code>my_task</code> in line 7. Other parameters are as follows:</p> +<ul> +<li> +<p><code>&quot;second_entry&quot;</code>: A descriptive name for the task. This is not used by FreeRTOS in any +way. It is included purely as a debugging aid.</p> +</li> +<li> +<p><code>1024</code>: The stack depth. Note the value is <code>number-of-bytes</code> / 4. Learning to choose the right size is an artform you pickup as you work through the examples.</p> +</li> +<li> +<p><code>NULL</code>: Parameter to our callback function. Here we're passing nothing.</p> +</li> +<li> +<p><code>15</code>: The task Priority. See below.</p> +</li> +<li> +<p><code>Null</code>: Not used.</p> +</li> +</ul> +<p>In this example we maintain our blinky program in main that toggles every 500ms. In addition to this we are creating a task for a secondary blinky on port D4, with a different frequency... This example shows how an off-the self kernel eases our multitasking.</p> +<h3 id="priorities">Priorities<a class="zola-anchor" href="#priorities" aria-label="Anchor link for: priorities">#</a></h3> +<p>The SDK sets the maximum number of priorities to 32. See configMAX_PRIORITIES in <code>bl_iot_sdk/components/platform/soc/bl602/freertos_riscv/config/FreeRTOSConfig.h</code>.</p> +<p>Higher the value: higher the priority. If two tasks have the same priority a round robin scheduling is done. +To modify the priority of a running task use <code>vTaskPrioritySet(n)</code>.</p> +<h2 id="queues">Queues<a class="zola-anchor" href="#queues" aria-label="Anchor link for: queues">#</a></h2> +<h2 id="semaphores">Semaphores<a class="zola-anchor" href="#semaphores" aria-label="Anchor link for: semaphores">#</a></h2> +<h3 id="mutex">Mutex<a class="zola-anchor" href="#mutex" aria-label="Anchor link for: mutex">#</a></h3> + + + + PWM + 2022-02-24T00:00:00+00:00 + 2022-02-24T00:00:00+00:00 + + https://ntn888.github.io/blog/pwm/ + <p><img src="/img/breather_led.gif" alt="Led Fade" /></p> +<h2 id="step-1-led-fade">Step 1: Led Fade<a class="zola-anchor" href="#step-1-led-fade" aria-label="Anchor link for: step-1-led-fade">#</a></h2> +<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit ipsum, bibendum eget semper ac, eleifend sit amet ex. In hac habitasse platea dictumst. Morbi tempus metus a nibh blandit condimentum. Quisque quis neque urna. Etiam eget sapien ac lacus accumsan tincidunt nec nec felis. Quisque placerat, justo vitae congue efficitur, est est volutpat magna, sit amet consectetur purus lorem sed neque. Ut aliquet elit a ultrices hendrerit. In hac habitasse platea dictumst. Pellentesque quis eros lacinia, porttitor justo a, suscipit erat. Etiam vestibulum nibh quis mattis laoreet. Aenean sed lacus in massa elementum dapibus. Maecenas at arcu condimentum, consectetur ipsum a, dignissim nibh. Nulla facilisi. Suspendisse potenti. Aliquam eleifend ultrices gravida. Proin lacinia pellentesque faucibus.</p> +<h2 id="led-fade-with-dma-interrupts-idle-cpu">Led Fade with DMA &amp; Interrupts (Idle CPU)<a class="zola-anchor" href="#led-fade-with-dma-interrupts-idle-cpu" aria-label="Anchor link for: led-fade-with-dma-interrupts-idle-cpu">#</a></h2> +<p>&lt; TODO &gt;</p> + + + + Serial UART + 2022-02-24T00:00:00+00:00 + 2022-02-24T00:00:00+00:00 + + https://ntn888.github.io/blog/serial/ + <p>We will study the UART peripheral here. First the basic polling mode.</p> +<pre class="z-code"><code><span class="z-text z-plain">#include &lt;stdio.h&gt; +#include &lt;hosal_uart.h&gt; +#include &lt;FreeRTOS.h&gt; +#include &lt;task.h&gt; + +//hosal_uart_dev_t uart_dev; + +HOSAL_UART_DEV_DECL(uart_dev, 0, 16, 7, 2000000); + +void main (void) +{ + char data[] = &quot;test\n&quot;; + + // uart setup (for info) + hosal_uart_init(&amp;uart_dev); + + while (1) { + hosal_uart_send(&amp;uart_dev, data, sizeof(data)); + vTaskDelay(500); + } +} +</span></code></pre> +<p>We begin by including the relevant header <code>hosal_uart.h</code>. The initialisation is done in 2 parts. In line 8, we use the macro to configure the settings as desired, including which uart peripheral we select in the second argument, here we select the uart0. +Next we simply call in line 16.</p> +<h2 id="interrupt-mode">Interrupt Mode<a class="zola-anchor" href="#interrupt-mode" aria-label="Anchor link for: interrupt-mode">#</a></h2> +<p>&lt; todo &gt;</p> + + + + Timers + 2022-02-24T00:00:00+00:00 + 2022-02-24T00:00:00+00:00 + + https://ntn888.github.io/blog/timers/ + <h2 id="tick-timing">Tick Timing<a class="zola-anchor" href="#tick-timing" aria-label="Anchor link for: tick-timing">#</a></h2> +<p>Basic timing, there is nothing much to it. We just need a couple of functions for basic timing. If you are familiar with tick based timing in another RTOS, it's exactly the same.</p> +<pre class="z-code"><code><span class="z-text z-plain">tickType ts, milliseconds_spent; + +ts = xTaskGetTickCount(); + +&lt; do stuff &gt; + +milliseconds_spent = (xTaskGetTickCount() - ts) / portTICK_RATE_MS; + +</span></code></pre> +<h2 id="hardware-timers-with-interrupts">Hardware Timers with Interrupts<a class="zola-anchor" href="#hardware-timers-with-interrupts" aria-label="Anchor link for: hardware-timers-with-interrupts">#</a></h2> + + + + Basic GPIO BL702 + 2022-02-23T00:00:00+00:00 + 2022-02-23T00:00:00+00:00 + + https://ntn888.github.io/blog/basic-gpio/ + <p>The BL702 chip has 15 GPIOs. Each pin can be selected as one of the following modes:</p> +<ul> +<li> +<p>ANALOG_MODE</p> +<p>Used as a function pin, input and output analog.</p> +</li> +<li> +<p>INPUT_PULL_UP</p> +<p>Input with an internal pull-up resistor - use with devices that actively drive the signal low - e.g. button connected to ground.</p> +</li> +<li> +<p>INPUT_PULL_DOWN</p> +<p>Input with an internal pull-down resistor - use with devices that actively drive the signal high - e.g. button connected to a power rail.</p> +</li> +<li> +<p>INPUT_HIGH_IMPEDANCE</p> +<p>Input - must always be driven, either actively or by an external pullup resistor.</p> +</li> +<li> +<p>OUTPUT_PUSH_PULL</p> +<p>Output actively driven high and actively driven low - must not be connected to other active outputs - e.g. LED output.</p> +</li> +<li> +<p>OUTPUT_OPEN_DRAIN_NO_PULL</p> +<p>Output actively driven low but is high-impedance when set high - can be connected to other open-drain/open-collector outputs. Needs an external pull-up resistor.</p> +</li> +<li> +<p>OUTPUT_OPEN_DRAIN_PULL_UP</p> +<p>Output actively driven low and is pulled high with an internal resistor when set high - can be connected to other open-drain/open-collector outputs.</p> +</li> +<li> +<p>OUTPUT_OPEN_DRAIN_AF</p> +<p>Alternate Function Open Drain Mode.</p> +</li> +<li> +<p>OUTPUT_PUSH_PULL_AF</p> +<p>Alternate Function Push Pull Mode.</p> +</li> +</ul> +<h2 id="using-gpio">Using GPIO<a class="zola-anchor" href="#using-gpio" aria-label="Anchor link for: using-gpio">#</a></h2> +<p><img src="/img/blinky_702.jpg" alt="blinky" /></p> +<p>The relevant functions are defined in <code>hosal_gpio.h</code>. Include this header before using any GPIO functions. An example application is as follows:</p> +<pre class="z-code"><code><span class="z-text z-plain">#include &lt;stdio.h&gt; +#include &lt;hosal_gpio.h&gt; +#include &lt;FreeRTOS.h&gt; +#include &lt;task.h&gt; + +static hosal_gpio_dev_t gp1; + +void main (void) +{ + // setup + gp1.port = 0; // &lt;== make sure led connected to pin D0! + gp1.config = OUTPUT_OPEN_DRAIN_NO_PULL; + hosal_gpio_init(&amp;gp1); + hosal_gpio_output_set(&amp;gp1, 1); + + uint8_t value = 1; + + while (1) { + hosal_gpio_output_set(&amp;gp1, value ); + value = !value; + vTaskDelay(500); + } +} + +</span></code></pre> +<p>Before we could use the GPIO API, the GPIO struct must be defined and assigned as in line6. Here we're naming it as <code>gp1</code> to reference later.</p> +<p>Then we must initialise and config a GPIO pin before we can use it. This tas is done in lines 11-13.</p> +<p>The possible value for the 'config' setting was introduced in the start of this article; see above. Finally call the function <code>hosal_gpio_init</code>. Now we can use the API to control them.</p> +<p>As basic functionality of GPIOs, we set the output levels using the following commands:</p> +<pre class="z-code"><code><span class="z-text z-plain">hosal_gpio_output_set(hosal_gpio_dev_t *gpio, uint8_t value) +</span></code></pre> +<p>Value 0 sets the pin in logical 0 / inactive state. Value 1 sets the pin in logical 1 / active state.</p> +<p>To read an input:</p> +<pre class="z-code"><code><span class="z-text z-plain">hosal_gpio_input_get(hosal_gpio_dev_t *gpio, uint8_t *value) +</span></code></pre> +<p>I've looked for a GPIO 'toggle' function to simplify this example, but unfortunately the API docs do not indicate any...</p> +<h2 id="interrupt-mode">Interrupt Mode<a class="zola-anchor" href="#interrupt-mode" aria-label="Anchor link for: interrupt-mode">#</a></h2> +<p>We look into interrupts using GPIO pins. Each GPIO can be set as an interrupt function. The main idea is to register a callback functionusing the Interrupt API, and define it's contents. As follows:</p> +<pre class="z-code"><code><span class="z-text z-plain">#include &lt;stdio.h&gt; +#include &lt;hosal_gpio.h&gt; +#include &lt;FreeRTOS.h&gt; +#include &lt;task.h&gt; + +static hosal_gpio_dev_t gp1; +static hosal_gpio_dev_t key1; +uint8_t value = 1; + +void key1_irq(void *arg) +{ + hosal_gpio_output_set(&amp;gp1, value ); + value = !value; +} + +void main (void) +{ + // setup + key1.port = 8; // the d8 button; note that it&#39;s active LOW + key1.config = INPUT_HIGH_IMPEDANCE; // button includes external pullup + hosal_gpio_init(&amp;key1); + hosal_gpio_irq_set(&amp;key1, HOSAL_IRQ_TRIG_NEG_PULSE, key1_irq, NULL); // &#39;key1_irq&#39; indicates the desired procedure at interrupt. + + gp1.port = 5; + gp1.config = OUTPUT_OPEN_DRAIN_PULL_UP; + hosal_gpio_init(&amp;gp1); + hosal_gpio_output_set(&amp;gp1, 1); + + while (1) { + } +} +</span></code></pre> +<p>Line 10 defines the callback function named <code>key1_irq</code>. Here we are simply toggling the D8 LED.</p> +<p>Initialising the Interrupt requires a string of statements beginning on line 19. Notably we initialise it as an input Pin as we covered in the previous section. Then finally use the 'irq-set' function to configure it and indicate our callback function. Note that we had to define the device on line 7.</p> +<blockquote> +<p>This is a very basic demonstration of push button with no debounce mechanism. The led may inadvertently toggle multiple times for a single push.</p> +</blockquote> +<h2 id="alt-function-mode">ALT Function Mode<a class="zola-anchor" href="#alt-function-mode" aria-label="Anchor link for: alt-function-mode">#</a></h2> +<p>This is the secondary mode of each GPIO PIN where we tie it to the required peripheral. We'll look at setting up the PINs for alternate function as we deal with the relevant peripheral.</p> + + + + Basic application structure + 2022-02-22T00:00:00+00:00 + 2022-02-22T00:00:00+00:00 + + https://ntn888.github.io/blog/basic-app-structure/ + <p>The <code>bl_iot_sdk</code> is the development toolkit provided for the development of applications for the BL70X and BL60X controllers. It is quite a pleasant environment built upon the following major components:</p> +<ul> +<li>freeRTOS kernel</li> +<li>lwIP stack</li> +<li>'HOSAL' API (based off AliOS-Things HAL)</li> +</ul> +<p>To acquire the SDK, use the following git command (preferably from the home folder):</p> +<pre class="z-code"><code><span class="z-text z-plain">git clone https://github.com/bouffalolab/bl_iot_sdk.git +</span></code></pre> +<blockquote> +<p><strong>ProTip</strong></p> +<p>The sdk docs are available at <a rel="nofollow noreferrer" href="https://bouffalolab.github.io/bl_iot_sdk/index.html">https://bouffalolab.github.io/bl_iot_sdk/index.html</a>. Use the firefox translate <a rel="nofollow noreferrer" href="https://addons.mozilla.org/en-US/firefox/addon/traduzir-paginas-web/">plugin</a> to view it in English. I found that the chrome version is a bit finnicky...</p> +</blockquote> +<hr /> +<p>A basic bl-iot-sdk project structure is as follows:</p> +<pre class="z-code"><code><span class="z-text z-plain">. +├── compile_commands.json +├── main +│   ├── bouffalo.mk +│   └── main.c +├── Makefile +└── proj_config.mk +</span></code></pre> +<p>One file may already be familiar to you: <code>compile_commands.json</code>. If not see <a href="https://ntn888.github.io/blog/dev-env/">this post</a> on how to setup a dev environment.</p> +<p><code>main.c</code> is the main application C file. It contains the <code>main()</code> function. In our blinky example: +{% highlight c linenos %} +#include &lt;stdio.h&gt; +#include &lt;hosal_gpio.h&gt; +#include &lt;FreeRTOS.h&gt; +#include &lt;task.h&gt;</p> +<p>static hosal_gpio_dev_t gp1;</p> +<p>void main (void) +{ +// setup +gp1.port = 0; // &lt;== make sure led connected to pin D0! +gp1.config = OUTPUT_OPEN_DRAIN_NO_PULL; +hosal_gpio_init(&amp;gp1); +hosal_gpio_output_set(&amp;gp1, 1);</p> +<p>uint8_t value = 1;</p> +<p>while (1) { +hosal_gpio_output_set(&amp;gp1, value ); +value = !value; +vTaskDelay(500); +} +}<br /> +{% endhighlight %} +The specifics of this file are not important to us right now. We will study the contents in the next post.</p> +<p>The other three makefiles are simply copied over from the official blinky sample, and are required to successfully build the project.</p> +<p>The main makefile <code>Makefile</code> in the project root, is slightly modified:</p> +<pre class="z-code"><code><span class="z-text z-plain"># +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := main +BL60X_SDK_PATH=~/bl_iot_sdk +CONFIG_CHIP_NAME=BL702 +export BL60X_SDK_PATH CONFIG_CHIP_NAME +PROJECT_PATH := $(abspath .) +PROJECT_BOARD := evb +export PROJECT_PATH PROJECT_BOARD +#CONFIG_TOOLPREFIX := + +-include ./proj_config.mk + +ifeq ($(origin BL60X_SDK_PATH), undefined) +BL60X_SDK_PATH_GUESS ?= $(shell pwd) +BL60X_SDK_PATH ?= $(BL60X_SDK_PATH_GUESS)/../../.. +$(info ****** Please SET BL60X_SDK_PATH ******) +$(info ****** Trying SDK PATH [$(BL60X_SDK_PATH)]) +endif + +COMPONENTS_BLSYS := bltime blfdt blmtd bloop loopset looprt +COMPONENTS_VFS := romfs + +SOC_DRV = $(shell echo $(CONFIG_CHIP_NAME) | tr A-Z a-z) + + +INCLUDE_COMPONENTS += $(SOC_DRV)_rf +INCLUDE_COMPONENTS += $(SOC_DRV)_freertos + +INCLUDE_COMPONENTS += $(SOC_DRV) $(SOC_DRV)_std +INCLUDE_COMPONENTS += hosal mbedtls_lts lwip cli vfs yloop utils blog blog_testc newlibc +INCLUDE_COMPONENTS += $(COMPONENTS_NETWORK) +INCLUDE_COMPONENTS += $(COMPONENTS_BLSYS) +INCLUDE_COMPONENTS += $(COMPONENTS_VFS) +INCLUDE_COMPONENTS += $(PROJECT_NAME) + +include $(BL60X_SDK_PATH)/make_scripts_riscv/project.mk +</span></code></pre> +<p>We have hardcoded the chip name and the sdk path. This makes the make command simpler. Also note that we have changed the <code>PROJECT_NAME</code> to a generic name: main. This avoids the need to modify the variable to match each project.</p> +<p>A quick tutorial on Make is viewable <a rel="nofollow noreferrer" href="https://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/">here</a>.</p> +<p>NOTE: The makefile variable <code>BL60X_SDK_PATH</code> assumes that you have cloned the sdk into your home directory. If not please modify this variable to reflect your chosen path.</p> +<p>In <code>proj_config.mk</code>:</p> +<pre class="z-code"><code><span class="z-text z-plain"> +#### +CONFIG_SYS_VFS_ENABLE:=1 +CONFIG_SYS_VFS_UART_ENABLE:=1 +CONFIG_SYS_AOS_CLI_ENABLE:=1 +CONFIG_SYS_AOS_LOOP_ENABLE:=1 +CONFIG_SYS_BLOG_ENABLE:=1 +CONFIG_SYS_DMA_ENABLE:=1 +CONFIG_SYS_USER_VFS_ROMFS_ENABLE:=0 +CONFIG_SYS_APP_TASK_STACK_SIZE:=4096 +CONFIG_SYS_APP_TASK_PRIORITY:=15 + + +CONFIG_SYS_COMMON_MAIN_ENABLE:=1 +CONFIG_BL702_USE_ROM_DRIVER:=1 +CONFIG_BUILD_ROM_CODE := 1 +CONFIG_USE_XTAL32K:=1 + + +LOG_ENABLED_COMPONENTS:= blog_testc hosal +</span></code></pre> +<p>In <code>bouffalo.mk</code>:</p> +<pre class="z-code"><code><span class="z-text z-plain"># +# &quot;main&quot; pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding &#39;include&#39; to include path.) + +include $(BL60X_SDK_PATH)/components/network/ble/ble_common.mk + +ifeq ($(CONFIG_ENABLE_PSM_RAM),1) +CPPFLAGS += -DCONF_USER_ENABLE_PSRAM +endif + +ifeq ($(CONFIG_ENABLE_CAMERA),1) +CPPFLAGS += -DCONF_USER_ENABLE_CAMERA +endif + +ifeq ($(CONFIG_ENABLE_BLSYNC),1) +CPPFLAGS += -DCONF_USER_ENABLE_BLSYNC +endif + +ifeq ($(CONFIG_ENABLE_VFS_SPI),1) +CPPFLAGS += -DCONF_USER_ENABLE_VFS_SPI +endif + +ifeq ($(CONFIG_ENABLE_VFS_ROMFS),1) +CPPFLAGS += -DCONF_USER_ENABLE_VFS_ROMFS +endif + +CPPFLAGS += -DCONF_USER_BL702 +</span></code></pre> +<blockquote> +<p>As a convenience you may simply download the following <a href="/assets/src.zip">project skeleton</a>. Extract it to a 'workspace' directory of choice; then whenever you want to initiate a new project, simply run: +<code>cp -a src/ my_proj/</code></p> +</blockquote> +<p>Once the above files are in place all you have to do is run:</p> +<pre class="z-code"><code><span class="z-text z-plain">make -j +</span></code></pre> +<p>Once your project compiles successfully; you may run <code>make flash</code> to burn the firmware (ensuring you have the chip in bootloader mode).</p> +<p>If you ever run into problems run <code>make clean</code> as the first step.</p> + + + + Setup a development environment + 2022-02-22T00:00:00+00:00 + 2022-02-22T00:00:00+00:00 + + https://ntn888.github.io/blog/dev-env/ + <p>In this guide we will be setting a development environment for the bl602 development. Neovim is used here but you may choose to your liking. The important requirement is to setup code completion in your editor that'll give ide like features. I find that this is actually useful in the initial phases to aid learning the new framework.</p> +<p>To do this, we are going to setup lsp on neovim. Lsp is native to neovim and only requires some config settings! But you dont have to do this yourself, there's a <a rel="nofollow noreferrer" href="https://github.com/nvim-lua/kickstart.nvim">github repository</a> with preset config file you can just copy. It also helps to configure sane defaults to other settings.</p> +<p>First install neovim. Then you need a language server, kickstarter uses clangd. Install this via your package manager. In ubuntu:</p> +<pre class="z-code"><code><span class="z-text z-plain">sudo apt install clangd +</span></code></pre> +<p>Finally copy the kickstarter config. That's it for installation. You may scout around kickstarter to personalise your neovim setup further.</p> +<p>You also need to install a small app called bear. Then in your project directory run:</p> +<pre class="z-code"><code><span class="z-text z-plain">make clean +bear -- make +</span></code></pre> +<p>This spits out a file called <code>compile_commands.json</code> in your project root. Now you will have super ide like features in Neovim that make programming much more pleasurable!</p> +<blockquote> +<p>clangd would throw out an error: <code>unknown argument: '-fstrict-volatile-bitfields'</code>; to avoid this you can do so by instructing clangd to remove that flag from the compile command. To do this create a file <code>~/.config/clangd/config.yaml</code> with the following contents:</p> +<pre class="z-code"><code><span class="z-text z-plain">CompileFlags: + Remove: [-fstrict-volatile-bitfields] +</span></code></pre> +</blockquote> + + + + THE Dev board you've been waiting for: $1.8 XT-ZB1 Zigbee & BLE devkit features BL702 RISC-V module + 2022-02-20T00:00:00+00:00 + 2022-02-20T00:00:00+00:00 + + https://ntn888.github.io/blog/xt-zb1-bl702/ + <p>I chanced upon the XT-ZB1 on ali-express on the lookout for the perfect IoT board... Coming at around 1.8usd this is more than perfect, this is a surprise! Made by the same company that makes the SOC for the DT-BL10, Bouffalo Labs, I think this is a new frontier in IoT development. I think we probably should thank the RISC-V movement for making such things possible.</p> +<p><img src="/img/XT-ZB1.jpg" alt="XT-ZB1 pinouts" /></p> +<p>The MCU specs at: BL702C 32-bit RISC-V microcontroller @ 144 MHz with FPU, 132KB RAM, 192KB ROM with 8MB embedded flash. That's decent enough for most of demanding applications.</p> +<p>There's one caveat however, the zigbee supporting SDK variant is buried in the indicated site: www.bl602.fun, one has to download the tar copy of the SDK there.</p> +<p>Debugging is achieved using the advertised Sipeed RV-Debugger also available on Aliexpress. Alternatively one may use the OpenOCD's ft232r technique discussed here in an earlier post.</p> + + + + NuttX Supported Boards + 2022-02-18T00:00:00+00:00 + 2022-02-18T00:00:00+00:00 + + https://ntn888.github.io/blog/nuttx-supported-boards/ + <p>NuttX supports a variety of boards. One of the reasons I've settled on Nuttx is it's front row support for cheap chinese boards. Namely the bluepill, esp32 and our favourite the RISC-V DT-BL10 from Doiting.</p> +<p>The official site page for <a rel="nofollow noreferrer" href="https://nuttx.apache.org/docs/latest/platforms/index.html">supported boards</a> is a stub. It infact fooled me at first...</p> +<p>You can simply traverse the <code>boards</code> folder to get an uptodate view of the supported boards. For completeness sake I'll list here the list of supported boards at the time of writing:</p> +<pre class="z-code"><code><span class="z-text z-plain">$ tree boards -dL 3 +boards +├── arm +│   ├── a1x +│   │   └── pcduino-a10 +│   ├── am335x +│   │   └── beaglebone-black +│   ├── c5471 +│   │   └── c5471evm +│   ├── cxd56xx +│   │   ├── common +│   │   ├── drivers +│   │   └── spresense +│   ├── dm320 +│   │   └── ntosd-dm320 +│   ├── efm32 +│   │   ├── efm32-g8xx-stk +│   │   ├── efm32gg-stk3700 +│   │   └── olimex-efm32g880f128-stk +│   ├── eoss3 +│   │   └── quickfeather +│   ├── imx6 +│   │   └── sabre-6quad +│   ├── imxrt +│   │   ├── imxrt1020-evk +│   │   ├── imxrt1050-evk +│   │   ├── imxrt1060-evk +│   │   ├── imxrt1064-evk +│   │   └── teensy-4.x +│   ├── kinetis +│   │   ├── freedom-k28f +│   │   ├── freedom-k64f +│   │   ├── freedom-k66f +│   │   ├── kwikstik-k40 +│   │   ├── teensy-3.x +│   │   ├── twr-k60n512 +│   │   └── twr-k64f120m +│   ├── kl +│   │   ├── freedom-kl25z +│   │   ├── freedom-kl26z +│   │   └── teensy-lc +│   ├── lc823450 +│   │   └── lc823450-xgevk +│   ├── lpc17xx_40xx +│   │   ├── lincoln60 +│   │   ├── lpc4088-devkit +│   │   ├── lpc4088-quickstart +│   │   ├── lpcxpresso-lpc1768 +│   │   ├── lx_cpu +│   │   ├── mbed +│   │   ├── mcb1700 +│   │   ├── olimex-lpc1766stk +│   │   ├── open1788 +│   │   ├── pnev5180b +│   │   ├── u-blox-c027 +│   │   └── zkit-arm-1769 +│   ├── lpc214x +│   │   ├── mcu123-lpc214x +│   │   └── zp214xpa +│   ├── lpc2378 +│   │   └── olimex-lpc2378 +│   ├── lpc31xx +│   │   ├── ea3131 +│   │   ├── ea3152 +│   │   └── olimex-lpc-h3131 +│   ├── lpc43xx +│   │   ├── bambino-200e +│   │   ├── lpc4330-xplorer +│   │   ├── lpc4337-ws +│   │   ├── lpc4357-evb +│   │   └── lpc4370-link2 +│   ├── lpc54xx +│   │   └── lpcxpresso-lpc54628 +│   ├── max326xx +│   │   └── max32660-evsys +│   ├── moxart +│   │   └── moxa +│   ├── nrf52 +│   │   ├── nrf52832-dk +│   │   ├── nrf52832-mdk +│   │   ├── nrf52832-sparkfun +│   │   ├── nrf52840-dk +│   │   ├── nrf52840-dongle +│   │   └── nrf52-feather +│   ├── nuc1xx +│   │   └── nutiny-nuc120 +│   ├── phy62xx +│   │   └── phy6222 +│   ├── rp2040 +│   │   ├── common +│   │   ├── pimoroni-tiny2040 +│   │   └── raspberrypi-pico +│   ├── s32k1xx +│   │   ├── s32k118evb +│   │   ├── s32k144evb +│   │   ├── s32k146evb +│   │   ├── s32k148evb +│   │   └── ucans32k146 +│   ├── sam34 +│   │   ├── arduino-due +│   │   ├── flipnclick-sam3x +│   │   ├── sam3u-ek +│   │   ├── sam4cmp-db +│   │   ├── sam4e-ek +│   │   ├── sam4l-xplained +│   │   ├── sam4s-xplained +│   │   └── sam4s-xplained-pro +│   ├── sama5 +│   │   ├── giant-board +│   │   ├── sama5d2-xult +│   │   ├── sama5d3x-ek +│   │   ├── sama5d3-xplained +│   │   └── sama5d4-ek +│   ├── samd2l2 +│   │   ├── arduino-m0 +│   │   ├── circuit-express +│   │   ├── samd20-xplained +│   │   ├── samd21-xplained +│   │   └── saml21-xplained +│   ├── samd5e5 +│   │   ├── metro-m4 +│   │   └── same54-xplained-pro +│   ├── samv7 +│   │   ├── common +│   │   ├── same70-qmtech +│   │   ├── same70-xplained +│   │   └── samv71-xult +│   ├── stm32 +│   │   ├── axoloti +│   │   ├── b-g431b-esc1 +│   │   ├── b-g474e-dpow1 +│   │   ├── clicker2-stm32 +│   │   ├── cloudctrl +│   │   ├── common +│   │   ├── emw3162 +│   │   ├── et-stm32-stamp +│   │   ├── fire-stm32v2 +│   │   ├── hymini-stm32v +│   │   ├── maple +│   │   ├── mikroe-stm32f4 +│   │   ├── nucleo-f103rb +│   │   ├── nucleo-f207zg +│   │   ├── nucleo-f302r8 +│   │   ├── nucleo-f303re +│   │   ├── nucleo-f303ze +│   │   ├── nucleo-f334r8 +│   │   ├── nucleo-f410rb +│   │   ├── nucleo-f412zg +│   │   ├── nucleo-f429zi +│   │   ├── nucleo-f446re +│   │   ├── nucleo-f4x1re +│   │   ├── nucleo-g431kb +│   │   ├── nucleo-g431rb +│   │   ├── nucleo-l152re +│   │   ├── olimexino-stm32 +│   │   ├── olimex-stm32-e407 +│   │   ├── olimex-stm32-h405 +│   │   ├── olimex-stm32-h407 +│   │   ├── olimex-stm32-p107 +│   │   ├── olimex-stm32-p207 +│   │   ├── olimex-stm32-p407 +│   │   ├── omnibusf4 +│   │   ├── photon +│   │   ├── shenzhou +│   │   ├── stm3210e-eval +│   │   ├── stm3220g-eval +│   │   ├── stm3240g-eval +│   │   ├── stm32butterfly2 +│   │   ├── stm32f103-minimum +│   │   ├── stm32f334-disco +│   │   ├── stm32f3discovery +│   │   ├── stm32f411e-disco +│   │   ├── stm32f411-minimum +│   │   ├── stm32f429i-disco +│   │   ├── stm32f4discovery +│   │   ├── stm32ldiscovery +│   │   ├── stm32_tiny +│   │   ├── stm32vldiscovery +│   │   └── viewtool-stm32f107 +│   ├── stm32f0l0g0 +│   │   ├── b-l072z-lrwan1 +│   │   ├── nucleo-f072rb +│   │   ├── nucleo-f091rc +│   │   ├── nucleo-g070rb +│   │   ├── nucleo-g071rb +│   │   ├── nucleo-l073rz +│   │   ├── stm32f051-discovery +│   │   └── stm32f072-discovery +│   ├── stm32f7 +│   │   ├── nucleo-144 +│   │   ├── stm32f746g-disco +│   │   ├── stm32f746-ws +│   │   └── stm32f769i-disco +│   ├── stm32h7 +│   │   ├── nucleo-h743zi +│   │   ├── nucleo-h743zi2 +│   │   └── stm32h747i-disco +│   ├── stm32l4 +│   │   ├── b-l475e-iot01a +│   │   ├── nucleo-l432kc +│   │   ├── nucleo-l452re +│   │   ├── nucleo-l476rg +│   │   ├── nucleo-l496zg +│   │   ├── stm32l476-mdk +│   │   ├── stm32l476vg-disco +│   │   └── stm32l4r9ai-disco +│   ├── stm32l5 +│   │   ├── drivers +│   │   ├── nucleo-l552ze +│   │   └── stm32l562e-dk +│   ├── stm32u5 +│   │   ├── b-u585i-iot02a +│   │   └── drivers +│   ├── str71x +│   │   └── olimex-strp711 +│   ├── tiva +│   │   ├── dk-tm4c129x +│   │   ├── eagle100 +│   │   ├── ekk-lm3s9b96 +│   │   ├── launchxl-cc1310 +│   │   ├── launchxl-cc1312r1 +│   │   ├── lm3s6432-s2e +│   │   ├── lm3s6965-ek +│   │   ├── lm3s8962-ek +│   │   ├── lm4f120-launchpad +│   │   ├── tm4c123g-launchpad +│   │   └── tm4c1294-launchpad +│   ├── tms570 +│   │   ├── launchxl-tms57004 +│   │   └── tms570ls31x-usb-kit +│   └── xmc4 +│   ├── xmc4500-relax +│   └── xmc4700-relax +├── avr +│   ├── at32uc3 +│   │   └── avr32dev1 +│   ├── at90usb +│   │   ├── micropendous3 +│   │   └── teensy-2.0 +│   └── atmega +│   ├── amber +│   ├── arduino-mega2560 +│   └── moteino-mega +├── dummy +├── hc +│   └── m9s12 +│   ├── demo9s12ne64 +│   └── ne64badge +├── mips +│   ├── pic32mx +│   │   ├── mirtoo +│   │   ├── pic32mx7mmb +│   │   ├── pic32mx-starterkit +│   │   ├── sure-pic32mx +│   │   └── ubw32 +│   └── pic32mz +│   ├── chipkit-wifire +│   ├── flipnclick-pic32mz +│   └── pic32mz-starterkit +├── misoc +│   └── lm32 +│   └── misoc +├── or1k +│   └── mor1kx +│   └── or1k +├── renesas +│   ├── m16c +│   │   └── skp16c26 +│   ├── rx65n +│   │   ├── rx65n +│   │   ├── rx65n-grrose +│   │   ├── rx65n-rsk1mb +│   │   └── rx65n-rsk2mb +│   └── sh1 +│   └── us7032evb1 +├── risc-v +│   ├── bl602 +│   │   └── bl602evb +│   ├── c906 +│   │   └── smartl-c906 +│   ├── esp32c3 +│   │   └── esp32c3-devkit +│   ├── fe310 +│   │   └── hifive1-revb +│   ├── k210 +│   │   └── maix-bit +│   ├── litex +│   │   └── arty_a7 +│   ├── mpfs +│   │   ├── common +│   │   ├── icicle +│   │   └── m100pfsevp +│   ├── qemu-rv +│   │   └── rv-virt +│   └── rv32m1 +│   └── rv32m1-vega +├── sim +│   └── sim +│   └── sim +├── sparc +│   ├── bm3803 +│   │   └── xx3803 +│   └── bm3823 +│   └── xx3823 +├── x86 +│   └── qemu +│   └── qemu-i486 +├── x86_64 +│   └── intel64 +│   └── qemu-intel64 +├── xtensa +│   ├── esp32 +│   │   ├── common +│   │   ├── esp32-devkitc +│   │   ├── esp32-ethernet-kit +│   │   ├── esp32-wrover-kit +│   │   └── ttgo_lora_esp32 +│   ├── esp32s2 +│   │   ├── common +│   │   └── esp32s2-saola-1 +│   └── esp32s3 +│   ├── common +│   └── esp32s3-devkit +├── z16 +│   └── z16f +│   └── z16f2800100zcog +└── z80 + ├── ez80 + │   ├── ez80f910200kitg + │   ├── ez80f910200zco + │   ├── makerlisp + │   └── z20x + ├── z180 + │   └── p112 + ├── z8 + │   ├── z8encore000zco + │   └── z8f64200100kit + └── z80 + └── z80sim + +337 directories +</span></code></pre> + + + + A DIY jtag debugger + 2022-02-16T00:00:00+00:00 + 2022-02-16T00:00:00+00:00 + <p>One of openocd's hidden perks is it could use a typical ftdi232rl USB to Serial converter into a makeshift JTAG programmer. Not only is this cheap, it's quick convenience than to wait and order a new programmer.</p> +<p>In this article we see how to put this to use...</p> + + + https://ntn888.github.io/blog/diy-jtag-debugger/ + <p>One of openocd's hidden perks is it could use a typical ftdi232rl USB to Serial converter into a makeshift JTAG programmer. Not only is this cheap, it's quick convenience than to wait and order a new programmer.</p> +<p>In this article we see how to put this to use...</p> +<span id="continue-reading"></span> +<p>First we have to install openocd if you haven't already. This could be done using your systems package manager. Or if you're installing manually be sure to enable the interface when building openocd in the following step:</p> +<pre class="z-code"><code><span class="z-text z-plain">./configure --enable-ft232r +</span></code></pre> +<p>Hookup your board to the ftdi board. Follow the following pinouts:</p> +<table><thead><tr><th>JTAG Pin</th><th>ftdi Pin</th></tr></thead><tbody> +<tr><td>TDI</td><td>RXD</td></tr> +<tr><td>TCK</td><td>TXD</td></tr> +<tr><td>TDO</td><td>RTS</td></tr> +<tr><td>TMS</td><td>CTS</td></tr> +<tr><td>TRST</td><td>DTR</td></tr> +<tr><td>SRST</td><td>DCD</td></tr> +</tbody></table> +<p>Here's a pic with the debugger connected to a DT-BL10 (RISC-V) board:</p> +<p><img src="/img/debugger.jpg" alt="ftdi connection" /></p> +<p>Then you need to fireup openocd with a config. To do this, create a file called <code>my_ft232r.cfg</code> somewhere in you home directory with the following contents:</p> +<pre class="z-code"><code><span class="z-text z-plain">adapter driver ft232r +adapter speed 3000 +ft232r_restore_serial 0x15 +</span></code></pre> +<p>Now fireup openocd:</p> +<pre class="z-code"><code><span class="z-text z-plain">openocd -f path/to/my_ft232r.cfg/ +</span></code></pre> +<p>Hopefully your target is detected!</p> + + + + Welcome + 2022-02-16T00:00:00+00:00 + 2022-02-16T00:00:00+00:00 + + https://ntn888.github.io/blog/welcome/ + <p>Hello and Welcome!</p> +<p>In this blog we are interested in all things embedded! Primarily the topics will revolve around the <a rel="nofollow noreferrer" href="https://github.com/RT-Thread/rt-thread">RT-Thread</a> RTOS and cheap parts from outlets such as AliExpress.</p> + + + \ No newline at end of file diff --git a/giscus_dark.css b/giscus_dark.css new file mode 100644 index 0000000..bba962f --- /dev/null +++ b/giscus_dark.css @@ -0,0 +1 @@ +main{--primary-default: 109, 155, 255;--bg-default: 22, 22, 24;--color-prettylights-syntax-comment: #8b949e;--color-prettylights-syntax-constant: #79c0ff;--color-prettylights-syntax-entity: #d2a8ff;--color-prettylights-syntax-storage-modifier-import: #c9d1d9;--color-prettylights-syntax-entity-tag: #7ee787;--color-prettylights-syntax-keyword: #ff7b72;--color-prettylights-syntax-string: #a5d6ff;--color-prettylights-syntax-variable: #ffa657;--color-prettylights-syntax-brackethighlighter-unmatched: #f85149;--color-prettylights-syntax-invalid-illegal-text: #f0f6fc;--color-prettylights-syntax-invalid-illegal-bg: #8e1519;--color-prettylights-syntax-carriage-return-text: #f0f6fc;--color-prettylights-syntax-carriage-return-bg: #b62324;--color-prettylights-syntax-string-regexp: #7ee787;--color-prettylights-syntax-markup-list: #f2cc60;--color-prettylights-syntax-markup-heading: #1f6feb;--color-prettylights-syntax-markup-italic: #c9d1d9;--color-prettylights-syntax-markup-bold: #c9d1d9;--color-prettylights-syntax-markup-deleted-text: #ffdcd7;--color-prettylights-syntax-markup-deleted-bg: #67060c;--color-prettylights-syntax-markup-inserted-text: #aff5b4;--color-prettylights-syntax-markup-inserted-bg: #033a16;--color-prettylights-syntax-markup-changed-text: #ffdfb6;--color-prettylights-syntax-markup-changed-bg: #5a1e02;--color-prettylights-syntax-markup-ignored-text: #c9d1d9;--color-prettylights-syntax-markup-ignored-bg: #1158c7;--color-prettylights-syntax-meta-diff-range: #d2a8ff;--color-prettylights-syntax-brackethighlighter-angle: #8b949e;--color-prettylights-syntax-sublimelinter-gutter-mark: #484f58;--color-prettylights-syntax-constant-other-reference-link: #a5d6ff;--color-btn-text: rgb(235 235 245 / 86%);--color-btn-bg: rgba(var(--bg-default), 1);--color-btn-border: rgba(var(--bg-default), 1);--color-btn-shadow: 0 1px 0 rgba(var(--bg-default), 1);--color-btn-inset-shadow: inset 0 1px 0 rgba(var(--bg-default), 1);--color-btn-hover-bg: rgba(var(--bg-default), 0.5);--color-btn-hover-border: rgba(var(--bg-default), 0.5);--color-btn-active-bg: rgba(var(--primary-default), 0.2);--color-btn-active-border: rgba(var(--primary-default), 1);--color-btn-selected-bg: rgba(var(--primary-default), 0.15);--color-btn-primary-text: rgb(255 255 255 / 100%);--color-btn-primary-bg: rgba(var(--primary-default), 0.45);--color-btn-primary-border: rgba(var(--primary-default), 0.5);--color-btn-primary-shadow: 0 1px 0 rgb(27 31 36 / 10%);--color-btn-primary-inset-shadow: inset 0 1px 0 hsl(0deg 0% 100% / 3%);--color-btn-primary-hover-bg: rgba(var(--primary-default), 0.53);--color-btn-primary-hover-border: rgba(var(--primary-default), 0.75);--color-btn-primary-selected-bg: rgba(var(--primary-default), 0.45);--color-btn-primary-selected-shadow: inset 0 1px 0 rgb(0 45 17 / 20%);--color-btn-primary-disabled-text: rgb(255 255 255 / 80%);--color-btn-primary-disabled-bg: rgba(var(--primary-default), 0.5);--color-btn-primary-disabled-border: rgba(var(--primary-default), 0.5);--color-action-list-item-default-hover-bg: rgb(177 186 196 / 12%);--color-segmented-control-bg: rgb(110 118 129 / 10%);--color-segmented-control-button-bg: #0d1117;--color-segmented-control-button-selected-border: rgba(var(--bg-default), 0.85);--color-fg-default: rgb(235 235 245 / 86%);--color-fg-muted: rgb(235 235 245 / 60%);--color-fg-subtle: rgb(235 235 245 / 50%);--color-canvas-default: rgb(30 30 32 / 100%);--color-canvas-overlay: rgb(30 30 32 / 100%);--color-canvas-inset: rgba(var(--bg-default), 0.85);--color-canvas-subtle: rgba(var(--bg-default), 1);--color-border-default: rgba(var(--bg-default), 0.85);--color-border-muted: rgb(175 184 193 / 20%);--color-neutral-muted: rgb(175 184 193 / 20%);--color-accent-fg: rgba(var(--primary-default), 0.85);--color-accent-emphasis: rgba(var(--primary-default), 0.95);--color-accent-muted: rgba(var(--primary-default), 0.4);--color-accent-subtle: rgba(var(--primary-default), 0.1);--color-success-fg: #3fb950;--color-attention-fg: #d29922;--color-attention-muted: rgb(187 128 9 / 40%);--color-attention-subtle: rgb(187 128 9 / 15%);--color-danger-fg: #f85149;--color-danger-muted: rgb(248 81 73 / 40%);--color-danger-subtle: rgb(248 81 73 / 10%);--color-primer-shadow-inset: 0 1px 0 rgba(var(--bg-default), 1), inset 0 1px 0 rgba(var(--bg-default), 1);--color-scale-gray-7: rgb(22 22 24 / 100%);--color-scale-blue-8: rgb(16 185 129 / 15%);/*! Extensions from @primer/css/alerts/flash.scss */--color-social-reaction-bg-hover: var(--color-scale-gray-7);--color-social-reaction-bg-reacted-hover: var(--color-scale-blue-8)}main .pagination-loader-container{background-image:url("https://github.com/images/modules/pulls/progressive-disclosure-line-dark.svg")}main .gsc-loading-image{background-image:url("https://github.githubassets.com/images/mona-loading-dark.gif")}.gsc-comment-box-buttons a{border-radius:.25rem !important} \ No newline at end of file diff --git a/giscus_light.css b/giscus_light.css new file mode 100644 index 0000000..660fa5c --- /dev/null +++ b/giscus_light.css @@ -0,0 +1 @@ +main{--primary-default: 109, 155, 255;--bg-default: 246, 246, 247;--color-prettylights-syntax-comment: #6e7781;--color-prettylights-syntax-constant: #0550ae;--color-prettylights-syntax-entity: #8250df;--color-prettylights-syntax-storage-modifier-import: #24292f;--color-prettylights-syntax-entity-tag: #116329;--color-prettylights-syntax-keyword: #cf222e;--color-prettylights-syntax-string: #0a3069;--color-prettylights-syntax-variable: #953800;--color-prettylights-syntax-brackethighlighter-unmatched: #82071e;--color-prettylights-syntax-invalid-illegal-text: #f6f8fa;--color-prettylights-syntax-invalid-illegal-bg: #82071e;--color-prettylights-syntax-carriage-return-text: #f6f8fa;--color-prettylights-syntax-carriage-return-bg: #cf222e;--color-prettylights-syntax-string-regexp: #116329;--color-prettylights-syntax-markup-list: #3b2300;--color-prettylights-syntax-markup-heading: #0550ae;--color-prettylights-syntax-markup-italic: #24292f;--color-prettylights-syntax-markup-bold: #24292f;--color-prettylights-syntax-markup-deleted-text: #82071e;--color-prettylights-syntax-markup-deleted-bg: #ffebe9;--color-prettylights-syntax-markup-inserted-text: #116329;--color-prettylights-syntax-markup-inserted-bg: #dafbe1;--color-prettylights-syntax-markup-changed-text: #953800;--color-prettylights-syntax-markup-changed-bg: #ffd8b5;--color-prettylights-syntax-markup-ignored-text: #eaeef2;--color-prettylights-syntax-markup-ignored-bg: #0550ae;--color-prettylights-syntax-meta-diff-range: #8250df;--color-prettylights-syntax-brackethighlighter-angle: #57606a;--color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f;--color-prettylights-syntax-constant-other-reference-link: #0a3069;--color-btn-text: #24292f;--color-btn-bg: rgba(var(--bg-default), 1);--color-btn-border: rgba(var(--bg-default), 1);--color-btn-shadow: 0 1px 0 rgba(var(--bg-default), 1);--color-btn-inset-shadow: inset 0 1px 0 rgba(var(--bg-default), 1);--color-btn-hover-bg: rgba(var(--bg-default), 0.5);--color-btn-hover-border: rgba(var(--bg-default), 0.5);--color-btn-active-bg: rgba(var(--primary-default), 0.2);--color-btn-active-border: rgba(var(--primary-default), 1);--color-btn-selected-bg: rgba(var(--primary-default), 0.15);--color-btn-primary-text: rgb(255 255 255 / 100%);--color-btn-primary-bg: rgba(var(--primary-default), 1);--color-btn-primary-border: rgba(var(--primary-default), 1);--color-btn-primary-shadow: 0 1px 0 rgb(31 35 40 / 10%);--color-btn-primary-inset-shadow: inset 0 1px 0 rgb(255 255 255 / 3%);--color-btn-primary-hover-bg: rgba(var(--primary-default), 0.9);--color-btn-primary-hover-border: rgba(var(--primary-default), 0.75);--color-btn-primary-selected-bg: rgba(var(--primary-default), 1);--color-btn-primary-selected-shadow: inset 0 1px 0 rgb(0 45 17 / 20%);--color-btn-primary-disabled-text: rgb(255 255 255 / 80%);--color-btn-primary-disabled-bg: rgba(var(--primary-default), 0.5);--color-btn-primary-disabled-border: rgba(var(--primary-default), 0.5);--color-action-list-item-default-hover-bg: rgb(208 215 222 / 32%);--color-segmented-control-bg: #eaeef2;--color-segmented-control-button-bg: #fff;--color-segmented-control-button-selected-border: rgba(var(--bg-default), 0.85);--color-fg-default: rgb(60 60 67);--color-fg-muted: rgb(60 60 67 / 75%);--color-fg-subtle: rgb(60 60 67 / 33%);--color-canvas-default: rgb(255 255 255);--color-canvas-overlay: rgb(255 255 255);--color-canvas-inset: rgba(var(--bg-default), 0.85);--color-canvas-subtle: rgba(var(--bg-default), 1);--color-border-default: rgba(var(--bg-default), 0.85);--color-border-muted: rgb(175 184 193 / 20%);--color-neutral-muted: rgb(175 184 193 / 20%);--color-accent-fg: rgba(var(--primary-default), 0.85);--color-accent-emphasis: rgba(var(--primary-default), 0.95);--color-accent-muted: rgba(var(--primary-default), 0.4);--color-accent-subtle: rgba(var(--primary-default), 0.1);--color-success-fg: #1a7f37;--color-attention-fg: #9a6700;--color-attention-muted: rgb(212 167 44 / 40%);--color-attention-subtle: #fff8c5;--color-danger-fg: #d1242f;--color-danger-muted: rgb(255 129 130 / 40%);--color-danger-subtle: #ffebe9;--color-primer-shadow-inset: 0 1px 0 rgba(var(--bg-default), 1), inset 0 1px 0 rgba(var(--bg-default), 1);--color-scale-gray-1: rgb(234 238 242 / 100%);--color-scale-blue-1: rgb(16 185 129 / 15%);/*! Extensions from @primer/css/alerts/flash.scss */--color-social-reaction-bg-hover: var(--color-scale-gray-1);--color-social-reaction-bg-reacted-hover: var(--color-scale-blue-1)}main .pagination-loader-container{background-image:url("https://github.com/images/modules/pulls/progressive-disclosure-line.svg")}main .gsc-loading-image{background-image:url("https://github.githubassets.com/images/mona-loading-default.gif")}.gsc-comment:not(.gsc-reply-box) .gsc-replies{border-radius:unset}.gsc-comment-box-buttons a{border-radius:.25rem !important} \ No newline at end of file diff --git a/hl-dark.css b/hl-dark.css new file mode 100644 index 0000000..d6c9651 --- /dev/null +++ b/hl-dark.css @@ -0,0 +1,74 @@ +/* + * theme "Tomorrow Night" generated by syntect + */ + +.z-code { + color: #c5c8c6; +} + +.z-comment, .z-string.z-quoted.z-double.z-block.z-python { + color: #999999; +} +.z-keyword.z-operator.z-class, .z-constant.z-other, .z-source.z-php.z-embedded.z-line { + color: #ced1cf; +} +.z-variable, .z-support.z-other.z-variable, .z-string.z-other.z-link, .z-string.z-regexp, .z-entity.z-name.z-tag, .z-entity.z-other.z-attribute-name, .z-meta.z-tag, .z-declaration.z-tag { + color: #a67878; +} +.z-constant.z-numeric, .z-constant.z-language, .z-support.z-constant, .z-constant.z-character, .z-variable.z-parameter, .z-punctuation.z-section.z-embedded, .z-keyword.z-other.z-unit { + color: #e08355; +} +.z-type, .z-entity.z-name.z-class, .z-entity.z-name.z-type.z-class, .z-support.z-type, .z-support.z-class { + color: #6a8f8a; +} +.z-string, .z-constant.z-other.z-symbol, .z-entity.z-other.z-inherited-class, .z-markup.z-heading { + color: #85ad74; +} +.z-keyword.z-operator, .z-constant.z-other.z-color { + color: #6a8f8a; +} +.z-entity.z-name.z-function, .z-meta.z-function-call, .z-support.z-function, .z-keyword.z-other.z-special-method, .z-meta.z-block-level { + color: #81a2be; +} +.z-keyword, .z-storage, .z-storage.z-type, .z-entity.z-name.z-tag.z-css { + color: #b294bb; +} +.z-invalid { + color: #ced2cf; + background-color: #df5f5f; +} +.z-meta.z-separator { + color: #ced2cf; + background-color: #82a3bf; +} +.z-invalid.z-deprecated { + color: #ced2cf; + background-color: #b798bf; +} +.z-markup.z-inserted.z-diff, .z-markup.z-deleted.z-diff, .z-meta.z-diff.z-header.z-to-file, .z-meta.z-diff.z-header.z-from-file { + color: #ffffff; +} +.z-markup.z-inserted.z-diff, .z-meta.z-diff.z-header.z-to-file { + color: #5fe375; + background-color: #000000; +} +.z-markup.z-deleted.z-diff, .z-meta.z-diff.z-header.z-from-file { + color: #d46565; +} +.z-meta.z-diff.z-header.z-from-file, .z-meta.z-diff.z-header.z-to-file { + color: #ffffff; + background-color: #4271ae; +} +.z-meta.z-diff.z-range { + color: #3e999f; +font-style: italic; +} +.z-markup.z-deleted { + color: #f92672; +} +.z-markup.z-inserted { + color: #a6e22e; +} +.z-markup.z-changed { + color: #967efb; +} diff --git a/hl-light.css b/hl-light.css new file mode 100644 index 0000000..bded504 --- /dev/null +++ b/hl-light.css @@ -0,0 +1,64 @@ +/* + * theme "Tomorrow" generated by syntect + */ + +.z-code { + color: #4d4d4c; +} + +.z-comment, .z-string.z-quoted.z-double.z-block.z-python { + color: #999999; +} +.z-keyword.z-operator.z-class, .z-constant.z-other, .z-source.z-php.z-embedded.z-line { + color: #666969; +} +.z-variable, .z-support.z-other.z-variable, .z-string.z-other.z-link, .z-string.z-regexp, .z-entity.z-name.z-tag, .z-entity.z-other.z-attribute-name, .z-meta.z-tag, .z-declaration.z-tag { + color: #a67878; +} +.z-constant.z-numeric, .z-constant.z-language, .z-support.z-constant, .z-constant.z-character, .z-variable.z-parameter, .z-punctuation.z-section.z-embedded, .z-keyword.z-other.z-unit { + color: #e08355; +} +.z-type, .z-entity.z-name.z-class, .z-entity.z-name.z-type.z-class, .z-support.z-type, .z-support.z-class { + color: #568a8f; +} +.z-string, .z-constant.z-other.z-symbol, .z-entity.z-other.z-inherited-class, .z-markup.z-heading { + color: #85ad74; +} +.z-keyword.z-operator, .z-constant.z-other.z-color { + color: #568a8f; +} +.z-entity.z-name.z-function, .z-meta.z-function-call, .z-support.z-function, .z-keyword.z-other.z-special-method, .z-meta.z-block-level { + color: #4271ae; +} +.z-keyword, .z-storage, .z-storage.z-type { + color: #8959a8; +} +.z-invalid { + color: #ffffff; + background-color: #df5f5f; +} +.z-meta.z-separator { + color: #ffffff; + background-color: #4271ae; +} +.z-invalid.z-deprecated { + color: #ffffff; + background-color: #8959a8; +} +.z-markup.z-inserted.z-diff, .z-markup.z-deleted.z-diff, .z-meta.z-diff.z-header.z-to-file, .z-meta.z-diff.z-header.z-from-file { + color: #ffffff; +} +.z-markup.z-inserted.z-diff, .z-meta.z-diff.z-header.z-to-file { + background-color: #65c23a; +} +.z-markup.z-deleted.z-diff, .z-meta.z-diff.z-header.z-from-file { + background-color: #c82829; +} +.z-meta.z-diff.z-header.z-from-file, .z-meta.z-diff.z-header.z-to-file { + color: #ffffff; + background-color: #4271ae; +} +.z-meta.z-diff.z-range { + color: #3e999f; +font-style: italic; +} diff --git a/icon/alert.svg b/icon/alert.svg new file mode 100644 index 0000000..0ab867b --- /dev/null +++ b/icon/alert.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/arrow-up.svg b/icon/arrow-up.svg new file mode 100644 index 0000000..f210d57 --- /dev/null +++ b/icon/arrow-up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/backlink.svg b/icon/backlink.svg new file mode 100644 index 0000000..1fa5351 --- /dev/null +++ b/icon/backlink.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/check.svg b/icon/check.svg new file mode 100644 index 0000000..dfbcaa3 --- /dev/null +++ b/icon/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/copy.svg b/icon/copy.svg new file mode 100644 index 0000000..21b1c15 --- /dev/null +++ b/icon/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/email.svg b/icon/email.svg new file mode 100644 index 0000000..c481715 --- /dev/null +++ b/icon/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/fingerprint.svg b/icon/fingerprint.svg new file mode 100644 index 0000000..84b985c --- /dev/null +++ b/icon/fingerprint.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/github.svg b/icon/github.svg new file mode 100644 index 0000000..d7f6dff --- /dev/null +++ b/icon/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/hand-heart-fill.svg b/icon/hand-heart-fill.svg new file mode 100644 index 0000000..5a8c14a --- /dev/null +++ b/icon/hand-heart-fill.svg @@ -0,0 +1 @@ + diff --git a/icon/important.svg b/icon/important.svg new file mode 100644 index 0000000..d4356a4 --- /dev/null +++ b/icon/important.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/moon.svg b/icon/moon.svg new file mode 100644 index 0000000..19f378e --- /dev/null +++ b/icon/moon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/note.svg b/icon/note.svg new file mode 100644 index 0000000..7e6118c --- /dev/null +++ b/icon/note.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/question.svg b/icon/question.svg new file mode 100644 index 0000000..9611d3a --- /dev/null +++ b/icon/question.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/rss.svg b/icon/rss.svg new file mode 100644 index 0000000..481a430 --- /dev/null +++ b/icon/rss.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/sun.svg b/icon/sun.svg new file mode 100644 index 0000000..c7cc2eb --- /dev/null +++ b/icon/sun.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/tip.svg b/icon/tip.svg new file mode 100644 index 0000000..36f7a57 --- /dev/null +++ b/icon/tip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/toc.svg b/icon/toc.svg new file mode 100644 index 0000000..91bd824 --- /dev/null +++ b/icon/toc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/twitter.svg b/icon/twitter.svg new file mode 100644 index 0000000..457a783 --- /dev/null +++ b/icon/twitter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon/warning.svg b/icon/warning.svg new file mode 100644 index 0000000..6b90b3e --- /dev/null +++ b/icon/warning.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/2tasks.gif b/img/2tasks.gif new file mode 100644 index 0000000..d0d4373 Binary files /dev/null and b/img/2tasks.gif differ diff --git a/img/4MB_Pico.jpg b/img/4MB_Pico.jpg new file mode 100644 index 0000000..3744ec5 Binary files /dev/null and b/img/4MB_Pico.jpg differ diff --git a/img/PCF8574.jpeg b/img/PCF8574.jpeg new file mode 100644 index 0000000..3a72294 Binary files /dev/null and b/img/PCF8574.jpeg differ diff --git a/img/Screenshot from 2022-11-28 04-27-22.png b/img/Screenshot from 2022-11-28 04-27-22.png new file mode 100644 index 0000000..ab47dec Binary files /dev/null and b/img/Screenshot from 2022-11-28 04-27-22.png differ diff --git a/img/XT-ZB1.jpg b/img/XT-ZB1.jpg new file mode 100644 index 0000000..9232e40 Binary files /dev/null and b/img/XT-ZB1.jpg differ diff --git a/img/accel_final_effect.gif b/img/accel_final_effect.gif new file mode 100644 index 0000000..aea4a32 Binary files /dev/null and b/img/accel_final_effect.gif differ diff --git a/img/apple-touch-icon.png b/img/apple-touch-icon.png new file mode 100644 index 0000000..9082a43 Binary files /dev/null and b/img/apple-touch-icon.png differ diff --git a/img/avatar.jpg b/img/avatar.jpg new file mode 100755 index 0000000..a2acc52 Binary files /dev/null and b/img/avatar.jpg differ diff --git a/img/begin_stm32.jpg b/img/begin_stm32.jpg new file mode 100644 index 0000000..bbfcc1c Binary files /dev/null and b/img/begin_stm32.jpg differ diff --git a/img/blinky_702.jpg b/img/blinky_702.jpg new file mode 100644 index 0000000..14b0796 Binary files /dev/null and b/img/blinky_702.jpg differ diff --git a/img/blue-pill_riot_oled.resized.png b/img/blue-pill_riot_oled.resized.png new file mode 100644 index 0000000..df906de Binary files /dev/null and b/img/blue-pill_riot_oled.resized.png differ diff --git a/img/breather_led.gif b/img/breather_led.gif new file mode 100644 index 0000000..7b6a9c7 Binary files /dev/null and b/img/breather_led.gif differ diff --git a/img/debugger.jpg b/img/debugger.jpg new file mode 100644 index 0000000..22899b2 Binary files /dev/null and b/img/debugger.jpg differ diff --git a/img/doom-cmds.pdf b/img/doom-cmds.pdf new file mode 100755 index 0000000..975ddc5 Binary files /dev/null and b/img/doom-cmds.pdf differ diff --git a/img/electronics_lab.jpg b/img/electronics_lab.jpg new file mode 100644 index 0000000..ae5b579 Binary files /dev/null and b/img/electronics_lab.jpg differ diff --git a/img/favicon-16x16.png b/img/favicon-16x16.png new file mode 100644 index 0000000..4b1c2e5 Binary files /dev/null and b/img/favicon-16x16.png differ diff --git a/img/favicon-32x32.png b/img/favicon-32x32.png new file mode 100644 index 0000000..4e5827e Binary files /dev/null and b/img/favicon-32x32.png differ diff --git a/img/graphana_dash.png b/img/graphana_dash.png new file mode 100644 index 0000000..f82451f Binary files /dev/null and b/img/graphana_dash.png differ diff --git a/img/idc_ribbon.jpg b/img/idc_ribbon.jpg new file mode 100644 index 0000000..5b6f166 Binary files /dev/null and b/img/idc_ribbon.jpg differ diff --git a/img/llama-gpt-pc.resized.png b/img/llama-gpt-pc.resized.png new file mode 100644 index 0000000..fe26581 Binary files /dev/null and b/img/llama-gpt-pc.resized.png differ diff --git a/img/lutis_gaming.resized.png b/img/lutis_gaming.resized.png new file mode 100644 index 0000000..5baad0e Binary files /dev/null and b/img/lutis_gaming.resized.png differ diff --git a/img/micro_bit.jpg b/img/micro_bit.jpg new file mode 100644 index 0000000..db6e753 Binary files /dev/null and b/img/micro_bit.jpg differ diff --git a/img/micro_bit_breakout.jpg b/img/micro_bit_breakout.jpg new file mode 100644 index 0000000..5a1710a Binary files /dev/null and b/img/micro_bit_breakout.jpg differ diff --git a/img/mpu6050conn.png b/img/mpu6050conn.png new file mode 100644 index 0000000..346a2cc Binary files /dev/null and b/img/mpu6050conn.png differ diff --git a/img/multimeter_using.jpeg b/img/multimeter_using.jpeg new file mode 100644 index 0000000..f47e90c Binary files /dev/null and b/img/multimeter_using.jpeg differ diff --git a/img/newposters.jpg b/img/newposters.jpg new file mode 100644 index 0000000..3223a3d Binary files /dev/null and b/img/newposters.jpg differ diff --git a/img/nrf_clone_pic.resized.png b/img/nrf_clone_pic.resized.png new file mode 100644 index 0000000..3f3175e Binary files /dev/null and b/img/nrf_clone_pic.resized.png differ diff --git a/img/nrf_clone_pinout.resized.png b/img/nrf_clone_pinout.resized.png new file mode 100644 index 0000000..21c68fd Binary files /dev/null and b/img/nrf_clone_pinout.resized.png differ diff --git a/img/oled.jpg b/img/oled.jpg new file mode 100644 index 0000000..d8bc1eb Binary files /dev/null and b/img/oled.jpg differ diff --git a/img/ripes_animation.gif b/img/ripes_animation.gif new file mode 100644 index 0000000..9b36fdb Binary files /dev/null and b/img/ripes_animation.gif differ diff --git a/img/rust_lang_logo_icon_169776.png b/img/rust_lang_logo_icon_169776.png new file mode 100644 index 0000000..73795b7 Binary files /dev/null and b/img/rust_lang_logo_icon_169776.png differ diff --git a/img/sipeedM0.jpg b/img/sipeedM0.jpg new file mode 100644 index 0000000..d436944 Binary files /dev/null and b/img/sipeedM0.jpg differ diff --git a/img/soldered_board.jpg b/img/soldered_board.jpg new file mode 100644 index 0000000..9b03a65 Binary files /dev/null and b/img/soldered_board.jpg differ diff --git a/img/src.zip b/img/src.zip new file mode 100644 index 0000000..61177dc Binary files /dev/null and b/img/src.zip differ diff --git a/img/unused_logo.png b/img/unused_logo.png new file mode 100755 index 0000000..de8de0b Binary files /dev/null and b/img/unused_logo.png differ diff --git a/img/wSONlPv.png b/img/wSONlPv.png new file mode 100644 index 0000000..f23060e Binary files /dev/null and b/img/wSONlPv.png differ diff --git a/img/world-electricity-production-2022.png b/img/world-electricity-production-2022.png new file mode 100644 index 0000000..ab2860e Binary files /dev/null and b/img/world-electricity-production-2022.png differ diff --git a/img/xeon-monitor-24-threads.png b/img/xeon-monitor-24-threads.png new file mode 100644 index 0000000..c24dfbc Binary files /dev/null and b/img/xeon-monitor-24-threads.png differ diff --git a/img/yay_evil_by_ultravioletbat_d1uicfh.jpg b/img/yay_evil_by_ultravioletbat_d1uicfh.jpg new file mode 100644 index 0000000..90c6876 Binary files /dev/null and b/img/yay_evil_by_ultravioletbat_d1uicfh.jpg differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..e9560cf --- /dev/null +++ b/index.html @@ -0,0 +1 @@ +Ajit Ananthadevan
avatar
Ajit Ananthadevan@ajit
Interested in everything Embedded.

Unleashing Potential: Your Next-Level Embedded Electronics Consulting

Hey there, fellow electronics aficionados! Are you ready to turbocharge your neat projects and bring your innovative ideas to life? I specialise in consulting services that are here to supercharge your commercial ventures with the expertise and precision they deserve.

Why Consult?

In the vast ocean of embedded electronics, it’s easy to feel like you're navigating uncharted waters. That’s where we step in. Our consulting services provide strategic guidance to ensure your products not only meet the market demand but set new benchmarks for innovation and efficiency.

Our embedded electronics consulting is your ticket to not just building but innovating. It's about making smarter choices, learning endlessly, and connecting deeply with the pulse of technology.

Partner with us for services that promise not just to build but to innovate and lead. Together, we can craft the next generation of embedded systems that drive your business forward.

Shoot us a mail!


In this blog we will be concerned with the world of embedded development. I see it that the tooling and the dev environments have come a long way in the last 10 years, and it is an exciting world! This blog will be more concerned from a hobbyiest / maker viewpoint, involving parts mostly sourced from the bargain site Aliexpress.

More specifically we will be looking at IoT development. More specifically the low power wireless devices; namely BLE, Zigbee and the open standard 802.15.4. (Why they didn't give it a human friendly name I dont know?!).

Most development walkthroughs will be done on Linux environment, specifically Debian 10/11. Linux oriented articles/guides will be scattered throughout. I'm also a Linux addict. And where possible we will take a open source/open standard approach. This tends to be relevant for longer, whereas proprietary products change or dissappear!

The development environments we will tackle are based on open source project components integrated into the respective IDE by the vendor: such as FreeRTOS and lwIP.

Even though this is a hobbyist blog, this one is a little different. We would not be using Arduino. Instead we would be using the above frameworks. This might be a controvesial choice, but I feel that closer-to-metal frameworks are more exciting.. This blog is an aide to move out from Arduino to more resource friendly frameworks such as FreeRTOS or RIOT-OS.

FreeRTOS is a real-time kernel that enables multitasking; makes the life of a firmware dev easier. Good resources about RTOS theory include:

  • Digi-key Youtube series
  • μC/OS-II User’s Manual Chapter 2: “Real-Time Systems Concepts” pp47. Accessible here is an excellent intro into the world of RTOS. It has slow paced, thorough treatment.

PS: An abandoned write-up on the linux based RTOS - Zephyr is accessible here. It currently covers just the basics...

PS: Based off my rants about the lack of cheap hobbyiest nRF52 boards; here's a PCB I adopted. Features a RESET button and SWD pinout; and LIPO battery charging (for those low power applications!! Why else would you want an integrated soulution?! :smiley: ). It houses the E73-2G4M08S1E modules sold in Taoboa for $2.50. It's open licensed but with interest I could accumulate locally for posting...

pcb screenshot

\ No newline at end of file diff --git a/js/lightense.min.js b/js/lightense.min.js new file mode 100644 index 0000000..1f979a6 --- /dev/null +++ b/js/lightense.min.js @@ -0,0 +1,2 @@ +/*! lightense-images v1.0.17 | © Tunghsiao Liu | MIT */ +!function (e, t) { "object" == typeof exports && "object" == typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define([], t) : "object" == typeof exports ? exports.Lightense = t() : e.Lightense = t() }(this, (function () { return e = { 352: e => { function t(e, t) { var n = Object.keys(e); if (Object.getOwnPropertySymbols) { var r = Object.getOwnPropertySymbols(e); t && (r = r.filter((function (t) { return Object.getOwnPropertyDescriptor(e, t).enumerable }))), n.push.apply(n, r) } return n } function n(e) { for (var n = 1; n < arguments.length; n++) { var i = null != arguments[n] ? arguments[n] : {}; n % 2 ? t(Object(i), !0).forEach((function (t) { r(e, t, i[t]) })) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(i)) : t(Object(i)).forEach((function (t) { Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(i, t)) })) } return e } function r(e, t, n) { return t in e ? Object.defineProperty(e, t, { value: n, enumerable: !0, configurable: !0, writable: !0 }) : e[t] = n, e } function i(e) { return (i = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (e) { return typeof e } : function (e) { return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e })(e) } var o = function () { "use strict"; var e, t = { time: 300, padding: 40, offset: 40, keyboard: !0, cubicBezier: "cubic-bezier(.2, 0, .1, 1)", background: "var(--bg-color-80, rgba(255, 255, 255, .98))", zIndex: 1e6, beforeShow: void 0, afterShow: void 0, beforeHide: void 0, afterHide: void 0 }, r = {}; function o(e) { var t = r[e]; if (t) { if ("function" != typeof t) throw "config.".concat(e, " must be a function!"); Reflect.apply(t, r, [r]) } } function a(e) { switch (i(e)) { case "undefined": throw "You need to pass an element!"; case "string": return document.querySelectorAll(e); case "object": return e } } function c(e) { var t = e.length; if (t) for (var n = 0; n < t; n++)s(e[n]); else s(e) } function s(e) { e.src && !e.classList.contains("lightense-target") && (e.classList.add("lightense-target"), e.addEventListener("click", (function (i) { if (r.keyboard && (i.metaKey || i.ctrlKey)) return window.open(e.src, "_blank"); !function (e) { if (r.target = e, r.target.classList.contains("lightense-open")) return g(); o("beforeShow"), r.scrollY = window.scrollY, function (e, t, n) { e.addEventListener(t, (function r(i) { Reflect.apply(n, this, i), e.removeEventListener(t, r) })) }(r.target, "transitionend", (function () { o("afterShow") })); var i = new Image; i.onload = function () { !function (e) { var n = e.width, i = e.height, o = window.pageYOffset || document.documentElement.scrollTop || 0, a = window.pageXOffset || document.documentElement.scrollLeft || 0, c = r.target.getBoundingClientRect(), s = n / c.width, d = window.innerWidth || document.documentElement.clientWidth || 0, l = window.innerHeight || document.documentElement.clientHeight || 0, u = r.target.getAttribute("data-lightense-padding") || r.target.getAttribute("data-padding") || r.padding, g = d > u ? d - u : d - t.padding, p = l > u ? l - u : l - t.padding, f = n / i, b = g / p; r.scaleFactor = n < g && i < p ? s : f < b ? p / i * s : g / n * s; var h = d / 2, m = o + l / 2, v = c.left + a + c.width / 2, y = c.top + o + c.height / 2; r.translateX = Math.round(h - v), r.translateY = Math.round(m - y) }(this), function () { r.target.classList.add("lightense-open"), r.wrap = document.createElement("div"), r.wrap.className = "lightense-wrap", setTimeout((function () { r.target.style.transform = "scale(" + r.scaleFactor + ")" }), 20), r.target.parentNode.insertBefore(r.wrap, r.target), r.wrap.appendChild(r.target), setTimeout((function () { r.wrap.style.transform = "translate3d(" + r.translateX + "px, " + r.translateY + "px, 0)" }), 20); var e = { cubicBezier: r.target.getAttribute("data-lightense-cubic-bezier") || r.cubicBezier, background: r.target.getAttribute("data-lightense-background") || r.target.getAttribute("data-background") || r.background, zIndex: r.target.getAttribute("data-lightense-z-index") || r.zIndex }, t = n(n({}, r), e); d("lightense-images-css-computed", "\n :root {\n --lightense-z-index: ".concat(t.zIndex - 1, ";\n --lightense-backdrop: ").concat(t.background, ";\n --lightense-duration: ").concat(t.time, "ms;\n --lightense-timing-func: ").concat(t.cubicBezier, ";\n }")), r.container.style.visibility = "visible", setTimeout((function () { r.container.style.opacity = "1" }), 20) }(), window.addEventListener("keyup", f, !1), window.addEventListener("scroll", p, !1), r.container.addEventListener("click", g, !1) }, i.src = r.target.src }(this) }), !1)) } function d(e, t) { var n = document.head || document.getElementsByTagName("head")[0]; document.getElementById(e) && document.getElementById(e).remove(); var r = document.createElement("style"); r.id = e, r.styleSheet ? r.styleSheet.cssText = t : r.appendChild(document.createTextNode(t)), n.appendChild(r) } function l() { d("lightense-images-css", "\n:root {\n --lightense-z-index: ".concat(r.zIndex - 1, ";\n --lightense-backdrop: ").concat(r.background, ";\n --lightense-duration: ").concat(r.time, "ms;\n --lightense-timing-func: ").concat(r.cubicBezier, ";\n}\n\n.lightense-backdrop {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n position: fixed;\n top: 0;\n left: 0;\n overflow: hidden;\n z-index: calc(var(--lightense-z-index) - 1);\n padding: 0;\n margin: 0;\n transition: opacity var(--lightense-duration) ease;\n cursor: zoom-out;\n opacity: 0;\n background-color: var(--lightense-backdrop);\n visibility: hidden;\n}\n\n@supports (-webkit-backdrop-filter: blur(30px)) {\n .lightense-backdrop {\n background-color: var(--lightense-backdrop);\n -webkit-backdrop-filter: blur(30px);\n }\n}\n\n@supports (backdrop-filter: blur(30px)) {\n .lightense-backdrop {\n background-color: var(--lightense-backdrop);\n backdrop-filter: blur(30px);\n }\n}\n\n.lightense-wrap {\n position: relative;\n transition: transform var(--lightense-duration) var(--lightense-timing-func);\n z-index: var(--lightense-z-index);\n pointer-events: none;\n}\n\n.lightense-target {\n cursor: zoom-in;\n transition: transform var(--lightense-duration) var(--lightense-timing-func);\n pointer-events: auto;\n}\n\n.lightense-open {\n cursor: zoom-out;\n}\n\n.lightense-transitioning {\n pointer-events: none;\n}")) } function u() { null === document.querySelector(".lightense-backdrop") ? (r.container = document.createElement("div"), r.container.className = "lightense-backdrop", document.body.appendChild(r.container)) : r.container = document.querySelector(".lightense-backdrop") } function g() { o("beforeHide"), window.removeEventListener("keyup", f, !1), window.removeEventListener("scroll", p, !1), r.container.removeEventListener("click", g, !1), r.target.classList.remove("lightense-open"), r.wrap.style.transform = "", r.target.style.transform = "", r.target.classList.add("lightense-transitioning"), r.container.style.opacity = "", setTimeout((function () { o("afterHide"), r.container.style.visibility = "", r.container.style.backgroundColor = "", r.wrap.parentNode.replaceChild(r.target, r.wrap), r.target.classList.remove("lightense-transitioning") }), r.time) } function p() { Math.abs(r.scrollY - window.scrollY) >= r.offset && g() } function f(e) { e.preventDefault(), 27 === e.keyCode && g() } return function (i) { var o = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}; e = a(i), r = n(n({}, t), o), l(), u(), c(e) } }(); e.exports = o } }, t = {}, function n(r) { var i = t[r]; if (void 0 !== i) return i.exports; var o = t[r] = { exports: {} }; return e[r](o, o.exports, n), o.exports }(352); var e, t })); \ No newline at end of file diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..3589fc0 --- /dev/null +++ b/js/main.js @@ -0,0 +1,166 @@ +function enableThemeToggle() { + const themeToggle = document.querySelector('#theme-toggle'); + const hlLink = document.querySelector('link#hl'); + const preferDark = window.matchMedia("(prefers-color-scheme: dark)"); + function toggleTheme(theme) { + if (theme == "dark") document.body.classList.add('dark'); else document.body.classList.remove('dark'); + if (hlLink) hlLink.href = `/hl-${theme}.css`; + themeToggle.innerHTML = theme == "dark" ? themeToggle.dataset.sunIcon : themeToggle.dataset.moonIcon; + localStorage.setItem("theme", theme); + toggleGiscusTheme(theme); + } + function toggleGiscusTheme(theme) { + const iframe = document.querySelector('iframe.giscus-frame'); + if (iframe) iframe.contentWindow.postMessage({ giscus: { setConfig: { theme: `${location.origin}/giscus_${theme}.css` } } }, 'https://giscus.app'); + } + function initGiscusTheme() { + toggleGiscusTheme(localStorage.getItem("theme") || (preferDark.matches ? "dark" : "light")); + window.removeEventListener('message', initGiscusTheme); + } + window.addEventListener('message', initGiscusTheme); + themeToggle.addEventListener('click', () => toggleTheme(localStorage.getItem("theme") == "dark" ? "light" : "dark")); + preferDark.addEventListener("change", e => toggleTheme(e.matches ? "dark" : "light")); + if (!localStorage.getItem("theme") && preferDark.matches) toggleTheme("dark"); + if (localStorage.getItem("theme") == "dark") toggleTheme("dark"); +} + +function enableNavFold() { + const nav = document.querySelector('header nav'); + if (!nav) return; + const toggler = nav.querySelector('#toggler'); + if (!toggler) return; + const foldItems = nav.querySelectorAll('.fold'); + toggler.addEventListener('click', () => { + if (window.innerWidth < 768 && [...foldItems].every(item => !item.classList.contains('shown'))) return; + foldItems.forEach(item => item.classList.toggle('shown')); + }); +} + +function enableOutdateAlert() { + const alert = document.querySelector('#outdate_alert'); + if (!alert) return; + const publish = document.querySelector('#publish'); + const updated = document.querySelector('#updated'); + const updateDate = new Date(updated ? updated.textContent : publish.textContent); + const intervalDays = Math.floor((Date.now() - updateDate.getTime()) / (24 * 60 * 60 * 1000)); + const alertDays = parseInt(alert.dataset.days); + if (intervalDays >= alertDays) { + const msg = alert.dataset.alertTextBefore + intervalDays + alert.dataset.alertTextAfter; + alert.querySelector('.content').textContent = msg; + alert.classList.remove('hidden'); + } +} + +function enableTocToggle() { + const tocToggle = document.querySelector('#toc-toggle'); + if (!tocToggle) return; + const aside = document.querySelector('aside'); + tocToggle.addEventListener('click', () => { + tocToggle.classList.toggle('active'); + aside.classList.toggle('shown'); + }); +} + +function enableTocIndicate() { + const toc = document.querySelector('aside nav'); + if (!toc) return; + const headers = document.querySelectorAll('h2, h3'); + const tocMap = new Map(); + headers.forEach(header => tocMap.set(header, toc.querySelector(`a[href="#${header.id}"]`))); + let actived = null; + const observer = new IntersectionObserver((entries) => entries.forEach(entry => { + if (entry.isIntersecting) { + const target = tocMap.get(entry.target); + if (target == actived) return; + if (actived) actived.classList.remove('active'); + target.classList.add('active'); + actived = target; + } + }), { rootMargin: '-9% 0px -90% 0px' }); + headers.forEach(header => observer.observe(header)); +} + +function addCopyBtns() { + const cfg = document.querySelector('#copy-cfg'); + if (!cfg) return; + const copyIcon = cfg.dataset.copyIcon; + const checkIcon = cfg.dataset.checkIcon; + document.querySelectorAll('pre').forEach(block => { + if (block.classList.contains('mermaid')) return; + const wrapper = document.createElement('div'); + wrapper.className = 'codeblock'; + const btn = document.createElement('button'); + btn.className = 'copy'; + btn.ariaLabel = 'copy'; + btn.innerHTML = copyIcon; + const copy = () => { + navigator.clipboard.writeText(block.textContent).then(() => { + btn.innerHTML = checkIcon; + btn.classList.add('copied'); + btn.removeEventListener('click', copy); + setTimeout(() => { + btn.innerHTML = copyIcon; + btn.classList.remove('copied'); + btn.addEventListener('click', copy); + }, 2000); + }); + }; + btn.addEventListener('click', copy); + wrapper.appendChild(block.cloneNode(true)); + wrapper.appendChild(btn); + block.replaceWith(wrapper); + }); +} + +function addBackToTopBtn() { + const backBtn = document.querySelector('#back-to-top'); + if (!backBtn) return; + const toTop = () => window.scrollTo({ top: 0, behavior: 'smooth' }); + const toggle = () => { + const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; + if (scrollTop > 200 && !backBtn.classList.contains('shown')) { + backBtn.classList.add('shown'); + backBtn.addEventListener('click', toTop); + } else if (scrollTop <= 200 && backBtn.classList.contains('shown')) { + backBtn.classList.remove('shown'); + backBtn.removeEventListener('click', toTop); + } + }; + window.addEventListener('scroll', toggle); + toggle(); +} + +function addFootnoteBacklink() { + const backlinkIcon = document.querySelector('.prose').dataset.backlinkIcon; + const footnotes = document.querySelectorAll('.footnote-definition'); + footnotes.forEach(footnote => { + const backlink = document.createElement('button'); + backlink.className = 'backlink'; + backlink.ariaLabel = 'backlink'; + backlink.innerHTML = backlinkIcon; + backlink.addEventListener('click', () => window.scrollTo({ + top: document.querySelector(`.footnote-reference a[href="#${footnote.id}"]`).getBoundingClientRect().top + window.scrollY - 50, + })); + footnote.appendChild(backlink); + }); +} + +function enableImgLightense() { + window.addEventListener("load", () => Lightense(".prose img", { background: 'rgba(43, 43, 43, 0.19)' })); +} + +//-------------------------------------------- + +enableThemeToggle(); +enableNavFold(); +if (document.body.classList.contains('post')) { + enableOutdateAlert(); + enableTocToggle(); + enableTocIndicate(); + addBackToTopBtn(); +} +if (document.querySelector('.prose')) { + addCopyBtns(); + addFootnoteBacklink(); + enableImgLightense(); +} diff --git a/main.css b/main.css new file mode 100644 index 0000000..b38c108 --- /dev/null +++ b/main.css @@ -0,0 +1 @@ +*,*::before,*::after{box-sizing:border-box}body{min-height:100%;font-family:var(--main-font);background-color:var(--bg-color);color:var(--text-color)}html{scroll-behavior:smooth}button:focus-visible,a:focus-visible{outline:var(--primary-color) solid 3px}::selection{background-color:var(--primary-color);color:var(--bg-color)}@media screen and (min-width: 425px){body::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar{width:4px;height:4px}::-webkit-scrollbar-track{background:rgba(0,0,0,0)}::-webkit-scrollbar-thumb{background:rgba(174,183,202,.631372549);border-radius:0px}::-webkit-scrollbar-thumb:hover{background:rgba(138,144,161,.631372549)}}.prose h1{font-size:1.5em;font-weight:bolder;margin:1em 0 .5em 0}.prose h2{font-size:1.3em;padding-top:65px;margin-top:-45px}.prose h3{font-size:1.1em;padding-top:65px;margin-top:-45px}.prose h4{font-size:1.05em;margin:30px 0 15px 0}.prose .zola-anchor{visibility:hidden;margin-left:.75em;font-size:.85em}.prose h1:hover a.zola-anchor,.prose h2:hover a.zola-anchor,.prose h3:hover a.zola-anchor,.prose h4:hover a.zola-anchor,.prose h5:hover a.zola-anchor,.prose h6:hover a.zola-anchor{visibility:visible}.prose p{font-size:1em;line-height:inherit;word-wrap:break-word}.prose a{color:var(--primary-color);text-decoration-color:var(--primary-pale-color);text-decoration-thickness:1.5px}.prose a:hover{text-decoration-color:var(--primary-color)}@media (max-width: 425px){.prose a{text-decoration-color:unset;text-decoration-thickness:auto}}.prose img{max-width:100%;display:block;margin:0 auto;border-radius:var(--img-border-radius)}.prose figure{margin:0 auto}.prose figcaption{width:100%;text-align:center;margin:5px auto}.prose blockquote{border-left:3px var(--primary-pale-color) solid;padding-left:16px;margin:0 0 24px 0;color:var(--text-pale-color)}.prose li::marker{color:var(--primary-color)}.prose hr{border:none;background-color:var(--primary-pale-color);height:3px;margin:36px 0}.prose table{width:100%;border-spacing:0;border:1px solid var(--primary-pale-color)}.prose thead{background-color:var(--primary-pale-color)}.prose th,.prose td{line-height:2;text-align:center;border:1px solid var(--primary-pale-color);padding:1px 10px}.prose p>code,.prose li>code,.prose td>code,.prose th>code{font-family:var(--code-font);font-size:.8em;padding:1px 6px;margin:0 2px;color:var(--primary-color);background-color:var(--primary-pale-color);border-radius:var(--inline-code-border-radius)}.prose pre{font-size:.8em;margin:1.25em 0;padding:12px 48px 12px 16px;line-height:1.5;border:1.5px solid var(--primary-color);overflow:auto}.prose pre code{font-family:var(--code-font)}.prose pre[data-linenos]{padding:12px 48px 12px 8px}.prose pre table{width:100%;border-collapse:collapse;border:none}.prose pre table th,.prose pre table td{line-height:1.5}.prose pre table tr td:first-of-type{color:var(--text-pale-color)}.prose pre table td{padding:0;padding-right:48px;text-align:initial;border:initial}.prose pre table td:nth-of-type(1){text-align:right;user-select:none;padding-right:1em}.prose pre table td:nth-of-type(1) mark::before{left:-8px;width:calc(100% + 1em + 8px)}.prose pre mark{display:block;color:inherit;background-color:rgba(0,0,0,0);position:relative;overflow:visible}.prose pre mark::before{pointer-events:none;content:"";position:absolute;top:0;bottom:0;width:calc(100% + 48px + 48px);background-color:var(--highlight-mark-color)}.prose pre.mermaid{border:none}.prose pre>code>mark::before{width:calc(100% + 48px + 16px);left:-16px}.prose .codeblock{margin:1em 0;position:relative;overflow:auto}.prose .codeblock pre{margin:0}.prose .codeblock .copy{z-index:9;position:absolute;right:.6em;top:.6em;width:24px;height:24px;padding:2px;cursor:pointer;background:rgba(0,0,0,0);border:none;color:var(--text-pale-color)}.prose .codeblock .copy.copied,.prose .codeblock .copy:hover{color:var(--primary-color)}.prose .codeblock .copy svg{width:20px;height:20px}.prose .codeblock-with-filename{margin-top:calc(0px - 1.2em - 24px - 1em - 1.5px)}.prose .codeblock-with-filename .filename{z-index:1;position:relative;top:calc(1.2em + 24px + 1em + 1.5px);padding:12px 16px;line-height:1.2;color:var(--text-pale-color);border-bottom:1.5px solid var(--primary-pale-color)}.prose .codeblock-with-filename pre>code{display:inline-block;min-width:100%;margin-top:3.2em}.prose .footnote-definition{display:flex}.prose .footnote-definition .footnote-definition-label{position:static;font-size:1em;line-height:inherit;vertical-align:auto;margin-right:.5em}.prose .footnote-definition .footnote-definition-label::after{content:"."}.prose .footnote-definition p{margin:0}.prose .footnote-definition button.backlink{border:none;background:none;display:flex;align-items:center;margin-left:.5em;color:var(--primary-color);cursor:pointer}.prose .callout{line-height:inherit;margin-bottom:20px;border:1.5px solid var(--primary-color);padding:6px 12px;display:flex;gap:12px}.prose .callout .icon{height:1.75em;display:flex;align-items:center}.prose .callout .content p{margin:0}.prose .callout .content p+p{margin:.2em 0 .5em}.prose .callout.note{color:var(--callout-note-color);border-color:var(--callout-note-color)}.prose .callout.important{color:var(--callout-important-color);border-color:var(--callout-important-color)}.prose .callout.warning{color:var(--callout-warning-color);border-color:var(--callout-warning-color)}.prose .callout.alert{color:var(--callout-alert-color);border-color:var(--callout-alert-color)}.prose .callout.question{color:var(--callout-question-color);border-color:var(--callout-question-color)}.prose .callout.tip{color:var(--callout-tip-color);border-color:var(--callout-tip-color)}.prose .mermaid{background:#fff}body.prose-page main{font-size:var(--paragraph-font-size);min-height:100vh;padding-top:45px;margin:0 auto;max-width:var(--main-max-width);display:flex;flex-direction:column;justify-content:space-between}body.prose-page article{padding:45px 15px 30px;font-size:var(--paragraph-font-size);line-height:var(--paragraph-line-height)}body.prose-page .giscus{padding:0 15px}.layout-list .category{font-size:1.3em;margin:2em 15px 1em}.layout-list .post-list{margin-top:25px}.layout-list .post{display:flex;justify-content:space-between;align-items:flex-end;gap:5px;padding:6px 15px;margin:1px 0;border-radius:2px;font-size:1.1em;text-decoration:none;color:var(--primary-color);-webkit-tap-highlight-color:rgba(0,0,0,0)}.layout-list .post:hover{background-color:var(--primary-pale-color)}@media (max-width: 768px){.layout-list .post{font-size:1em;margin:2.5px 5px;padding:5px 10px}}.layout-list .post .date{white-space:nowrap}body.homepage #wrapper{width:100%;min-height:100vh;display:flex;justify-content:center;align-items:center}body.homepage main{width:100%;max-width:var(--homepage-max-width);padding:3em 0 6em}body.homepage #info{padding:0 15px;display:flex;gap:1em}body.homepage #info img{height:var(--avatar-size);width:var(--avatar-size);border-radius:50%}body.homepage #info #text{font-size:1.25em;display:flex;flex-direction:column;justify-content:space-around}body.homepage #info #id{margin-left:.5em;color:var(--primary-color)}body.homepage #info #bio{font-size:.85em}body.homepage #links{padding:0 15px;margin:1.25em 0 1.75em;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:.5em 1em;font-size:1.25em}body.homepage #links a,body.homepage #links button{text-decoration:none;color:var(--text-color)}body.homepage #links a:hover,body.homepage #links button:hover{color:var(--primary-color)}body.homepage #links #left{display:flex;flex-wrap:wrap;gap:1em}body.homepage #links #left a{border-bottom:1.5px solid var(--primary-color);line-height:24px}body.homepage #links #right{display:flex;gap:.5em;margin-left:auto}body.homepage #links #right a,body.homepage #links #right button{width:24px;height:24px}body.homepage #links #right button{padding:0;border:none;background-color:rgba(0,0,0,0);cursor:pointer}@media (max-width: 425px){body.homepage #links #left{gap:.75em}}body.homepage #brief{padding:0 15px;font-size:1.15em;line-height:1.5}header{z-index:99;position:fixed;top:0;width:100%;height:45px;background-color:var(--bg-color);border-bottom:1.5px solid var(--primary-color)}@supports (-webkit-backdrop-filter: none) or (backdrop-filter: none){header.blur{background-color:initial;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}}header #header-wrapper{height:100%;max-width:var(--main-max-width);margin:0 auto;padding:0 15px;display:flex;justify-content:space-between;align-items:center;position:relative}header nav{font-size:1.4em;display:flex;align-items:center}header nav a{color:var(--text-color);text-decoration:none}header nav a:hover{color:var(--primary-color)}header nav .separator{width:24px;text-align:center;line-height:1;cursor:pointer;border:none;padding:0;background:rgba(0,0,0,0);margin:0 .1em;color:var(--text-color);-webkit-tap-highlight-color:rgba(0,0,0,0)}header nav .wrap.left{margin:0 .3em 0 -.1em}header nav .wrap.right{margin:0 0 0 .3em}header nav .wrap-separator{margin:0 .5em 0 .1em}header nav .fold{display:none}header nav .fold.shown{display:initial}header #btns{display:flex;gap:1em;align-items:center;padding-top:1.5px}header #btns a,header #btns button{width:24px;height:24px;border:none;background-color:rgba(0,0,0,0);padding:0;text-decoration:none;color:var(--text-color);cursor:pointer}@media (hover: hover){header #btns a:hover,header #btns button:hover{color:var(--primary-color)}}header #toc-toggle{display:none}@media (max-width: 1024px){header #toc-toggle{display:inline-block}header #toc-toggle.active{color:var(--primary-color)}}footer{font-size:.95rem;padding:15px;display:flex;justify-content:center;align-items:center;flex-wrap:wrap;gap:15px;color:var(--text-pale-color)}footer .copyright{margin-right:auto}footer .credits{margin-left:auto}footer a{color:var(--text-pale-color)}@media (max-width: 374px){footer{flex-direction:column;gap:0}footer .copyright,footer .credits{margin:0}}body.blog #wrapper{margin:0 auto;max-width:var(--main-max-width);min-height:100vh;display:flex;flex-direction:column;justify-content:space-between}body.blog main{margin:60px 0}body.post #wrapper{display:flex;justify-content:space-between;font-size:18px}body.post #blank{order:1;position:sticky;width:calc((100% - var(--main-max-width))/2)}body.post main{order:2;width:100%;margin:0 auto;padding-top:45px;max-width:var(--main-max-width);min-height:100vh;display:flex;flex-direction:column;justify-content:space-between}body.post article{padding:0 15px 30px;font-size:var(--paragraph-font-size);line-height:var(--paragraph-line-height)}body.post article #post-info{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;margin-bottom:1em}body.post article #date{color:var(--text-pale-color);margin-bottom:1em}body.post article #date #publish,body.post article #date #updated{margin-right:20px}body.post article #tags{margin-bottom:1em;display:flex;gap:1em;flex-wrap:wrap}body.post article #tags a{color:var(--primary-color);text-decoration:none}body.post article #tags a span{font-size:.95em;margin-right:2px}body.post article #outdate_alert.hidden{display:none}body.post .mermaid{background:#fff}body.post .giscus{padding:0 15px}body.post aside{order:3;width:calc((100% - var(--main-max-width))/2);position:sticky;margin-top:195px;top:60px;height:min-content;font-size:var(--aside-font-size)}body.post aside nav{padding:5px 15px 5px 2em;min-width:60%;overflow-y:auto;max-height:calc(100vh - 50px - 5em);scrollbar-width:none}body.post aside nav::-webkit-scrollbar{width:0}body.post aside nav:hover::-webkit-scrollbar{width:4px}body.post aside ul{list-style-type:none;padding:0;line-height:2;margin:0}body.post aside a{text-decoration:none;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-pale-color);position:relative;padding:0 1em}body.post aside a.h3{padding-left:2.5em;font-size:.95em}body.post aside a::before{display:block;content:"";width:2px;position:absolute;top:.6em;bottom:.7em;left:0em;background:rgba(0,0,0,0)}body.post aside a:hover,body.post aside a.active{color:var(--primary-color)}body.post aside a:hover::before{background-color:var(--primary-color)}body.post aside #back-to-top{z-index:99;height:28px;padding:0;position:fixed;bottom:1.5em;margin-left:3em;color:var(--text-pale-color);background:rgba(0,0,0,0);border:none;cursor:pointer;transform:translateY(-5px) scale(0);transition:transform .2s}body.post aside #back-to-top svg{width:28px;height:28px}body.post aside #back-to-top.shown{transform:translateY(0px) scale(1)}body.post aside #back-to-top:hover{color:var(--primary-color)}@media screen and (max-width: 1024px){body.post aside{position:fixed;right:-100%;top:45px;margin-top:0;min-width:260px;height:100%;background-color:var(--bg-color);box-shadow:rgba(0,0,0,.08) -2px 8px 8px 0px}body.post aside.shown{right:0}body.post aside nav{padding:1em 0}body.post aside ul{padding-left:10px}body.post aside #back-to-top{display:none}@supports (-webkit-backdrop-filter: none) or (backdrop-filter: none){body.post aside.blur{background-color:fade(var(--bg-color), 75%);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}}body.post #blank{display:none}body.post main{margin:0 auto}}body.tag-list #wrapper{margin:0 auto;max-width:var(--main-max-width);min-height:100vh;display:flex;flex-direction:column;justify-content:space-between}body.tag-list main{margin:85px auto 60px}body.tag-list .title{width:min-content;font-size:1.3em;margin:0 auto}body.tag-list .tags{margin-top:40px;display:flex;justify-content:center;align-items:center;flex-wrap:wrap;font-size:1.1em}body.tag-list .tags a{color:var(--primary-color);text-decoration:none;margin:2em}body.tag-single #wrapper{margin:0 auto;max-width:var(--main-max-width);min-height:100vh;display:flex;flex-direction:column;justify-content:space-between}body.tag-single main{width:100%;margin:85px auto 60px}body.tag-single .title{width:min-content;white-space:nowrap;color:var(--text-color);font-size:1.3em;margin:0 auto;margin-bottom:40px;padding:0 0 0 60px}body.tag-single .title a{margin-left:30px;font-size:.5em;color:var(--primary-color)}body.tag-single .post{display:flex;justify-content:space-between;align-items:flex-end;gap:5px;padding:6px 15px;margin:1px 0;border-radius:2px;font-size:1.1em;text-decoration:none;color:var(--primary-color);-webkit-tap-highlight-color:rgba(0,0,0,0)}body.tag-single .post:hover{background-color:var(--primary-pale-color)}body.tag-single .post .date{white-space:nowrap}@media screen and (max-width: 768px){body.tag-single .post{font-size:1em;margin:2.5px 5px;padding:5px 10px}}body.projects #wrapper{margin:0 auto;max-width:var(--main-max-width);min-height:100vh;display:flex;flex-direction:column;justify-content:space-between}body.projects main{width:100%;margin:90px auto 40px}body.projects .proj{margin:0px 15px 45px}body.projects .proj .name{font-size:1.2em;margin:0 0 15px 0;color:var(--primary-color)}body.projects .proj .desc{line-height:1.5;margin:0 0 10px 0}body.projects .proj .desc a{color:var(--primary-color);text-decoration:underline var(--primary-pale-color) 2px}body.projects .proj .desc a:hover{text-decoration-color:var(--primary-color)}body.projects .proj .desc p>code{font-family:var(--code-font);font-size:.8em;padding:1px 6px;color:var(--primary-color);background-color:var(--primary-pale-color);border-radius:4px}body.projects .proj .more{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:1em}body.projects .proj .more .tags{font-size:.9em;display:flex;gap:.5em 1em;flex-wrap:wrap}body.projects .proj .more .tags div{color:var(--text-pale-color)}body.projects .proj .more .tags div span{font-size:.9em;margin-right:1.5px}body.projects .proj .more .links{margin-left:auto;display:flex;justify-content:end;gap:.5em 1em}body.projects .proj .more .links a{text-decoration:none;border-bottom:1.5px solid var(--primary-color);padding:0 2px;cursor:pointer;color:inherit}body.projects .proj .more .links a:visited{color:inherit}body.projects .proj .more .links a:hover{color:var(--primary-color)}body.not-found{height:100vh;height:100dvh;display:flex;justify-content:center;align-items:center}body.not-found .wrapper{display:flex;flex-direction:column;align-items:center;gap:3em}body.not-found .error{display:flex;align-items:center;font-size:2em;color:var(--text-pale-color)}body.not-found .spacer{background:var(--text-pale-color);width:2px;height:.75em;margin:0 .5em}body.not-found .text{font-size:.85em}body.not-found a{color:var(--primary-color);text-decoration-color:var(--primary-pale-color)}body.not-found a:hover,body.not-found a:active{text-decoration-color:var(--primary-color)}html{line-height:1.15;-webkit-text-size-adjust:100%;text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:rgba(0,0,0,0)}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none} \ No newline at end of file diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..91e91e6 --- /dev/null +++ b/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Disallow: +Allow: / +Sitemap: https://ntn888.github.io/sitemap.xml diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..7f6cc69 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,480 @@ + + + + https://ntn888.github.io/ + + + https://ntn888.github.io/blog/ + + + https://ntn888.github.io/blog/802-15-4-wireless/ + 2023-04-22 + + + https://ntn888.github.io/blog/aliexpress-intro/ + 2023-11-10T10:27:00 + + + https://ntn888.github.io/blog/analog-filters-primer/ + 2022-02-27 + + + https://ntn888.github.io/blog/basic-app-structure/ + 2022-02-22 + + + https://ntn888.github.io/blog/basic-gpio/ + 2022-02-23 + + + https://ntn888.github.io/blog/begin-embedded/ + 2023-11-05T18:27:00 + + + https://ntn888.github.io/blog/bsp-config/ + 2022-03-15 + + + https://ntn888.github.io/blog/building-lab/ + 2023-11-09T20:27:00 + + + https://ntn888.github.io/blog/c-linkerscripts/ + 2022-11-16 + + + https://ntn888.github.io/blog/debian-testing/ + 2023-05-11 + + + https://ntn888.github.io/blog/debug-bl702/ + 2022-03-17 + + + https://ntn888.github.io/blog/dev-env/ + 2022-02-22 + + + https://ntn888.github.io/blog/dev-station/ + 2023-05-23 + + + https://ntn888.github.io/blog/device-drivers-101/ + 2022-02-24 + + + https://ntn888.github.io/blog/diy-jtag-debugger/ + 2022-02-16 + + + https://ntn888.github.io/blog/diy-nas/ + 2023-05-10 + + + https://ntn888.github.io/blog/doom/ + 2022-11-16 + + + https://ntn888.github.io/blog/drop-ship/ + 2023-05-17 + + + https://ntn888.github.io/blog/esp32c3/ + 2022-03-18 + + + https://ntn888.github.io/blog/freertos/ + 2022-02-24 + + + https://ntn888.github.io/blog/green-energy/ + 2023-05-21 + + + https://ntn888.github.io/blog/ipv6-box/ + 2023-09-16 + + + https://ntn888.github.io/blog/linux-gaming/ + 2023-11-09T17:27:00 + + + https://ntn888.github.io/blog/llama-howto/ + 2023-11-14T00:27:00 + + + https://ntn888.github.io/blog/llama/ + 2023-11-10T03:27:00 + + + https://ntn888.github.io/blog/llama2/ + 2023-11-12T22:27:00 + + + https://ntn888.github.io/blog/mastodon/ + 2022-11-13 + + + https://ntn888.github.io/blog/mcu-selection-guide/ + 2022-03-16 + + + https://ntn888.github.io/blog/micro-bit-breakout/ + 2023-05-05 + + + https://ntn888.github.io/blog/multimeters/ + 2023-11-09T14:27:00 + + + https://ntn888.github.io/blog/net-commands/ + 2023-05-16 + + + https://ntn888.github.io/blog/nrf-clones/ + 2023-11-07T20:27:00 + + + https://ntn888.github.io/blog/nrfmicro/ + 2022-03-20 + + + https://ntn888.github.io/blog/nuttx-supported-boards/ + 2022-02-18 + + + https://ntn888.github.io/blog/octave-cli/ + 2022-02-27 + + + https://ntn888.github.io/blog/pico-4m/ + 2022-11-04 + + + https://ntn888.github.io/blog/piracy/ + 2022-11-24 + + + https://ntn888.github.io/blog/postfix-mail/ + 2022-12-27 + + + https://ntn888.github.io/blog/project-gravity-detector/ + 2023-05-06 + + + https://ntn888.github.io/blog/pwm/ + 2022-02-24 + + + https://ntn888.github.io/blog/riot-oled/ + 2023-11-10T01:27:00 + + + https://ntn888.github.io/blog/selfhosted-gitlab/ + 2022-11-23 + + + https://ntn888.github.io/blog/serial/ + 2022-02-24 + + + https://ntn888.github.io/blog/sipeed-bl702/ + 2022-12-01 + + + https://ntn888.github.io/blog/sphinx/ + 2022-12-07 + + + https://ntn888.github.io/blog/ssd1306/ + 2022-03-17 + + + https://ntn888.github.io/blog/ssg-comparison/ + 2023-11-05T15:27:00 + + + https://ntn888.github.io/blog/thoughts-esp-idf/ + 2022-03-19 + + + https://ntn888.github.io/blog/timers/ + 2022-02-24 + + + https://ntn888.github.io/blog/welcome/ + 2022-02-16 + + + https://ntn888.github.io/blog/x99-motherboards/ + 2023-11-08T11:27:00 + + + https://ntn888.github.io/blog/xt-zb1-bl702/ + 2022-02-20 + + + https://ntn888.github.io/blog/zola-review/ + 2023-11-05T06:27:00 + + + https://ntn888.github.io/blog/zola-switch/ + 2023-11-03 + + + https://ntn888.github.io/categories/ + + + https://ntn888.github.io/categories/asm/ + + + https://ntn888.github.io/categories/bl702/ + + + https://ntn888.github.io/categories/misc/ + + + https://ntn888.github.io/categories/nrf/ + + + https://ntn888.github.io/categories/nuttx/ + + + https://ntn888.github.io/categories/riotos/ + + + https://ntn888.github.io/categories/update/ + + + https://ntn888.github.io/tags/ + + + https://ntn888.github.io/tags/accelerometer/ + + + https://ntn888.github.io/tags/ai/ + + + https://ntn888.github.io/tags/analog/ + + + https://ntn888.github.io/tags/assembly/ + + + https://ntn888.github.io/tags/bargain-diy/ + + + https://ntn888.github.io/tags/bl702/ + + + https://ntn888.github.io/tags/blog/ + + + https://ntn888.github.io/tags/blue-pill/ + + + https://ntn888.github.io/tags/debug/ + + + https://ntn888.github.io/tags/dev-environment/ + + + https://ntn888.github.io/tags/development/ + + + https://ntn888.github.io/tags/device-drivers/ + + + https://ntn888.github.io/tags/diy/ + + + https://ntn888.github.io/tags/documentation/ + + + https://ntn888.github.io/tags/drop-ship/ + + + https://ntn888.github.io/tags/electronic-filter/ + + + https://ntn888.github.io/tags/electronics-lab/ + + + https://ntn888.github.io/tags/electronics/ + + + https://ntn888.github.io/tags/emacs/ + + + https://ntn888.github.io/tags/embedded-hobbiest/ + + + https://ntn888.github.io/tags/embedded-hobbyist/ + + + https://ntn888.github.io/tags/energy-production/ + + + https://ntn888.github.io/tags/environment/ + + + https://ntn888.github.io/tags/fimware-develompent/ + + + https://ntn888.github.io/tags/freertos/ + + + https://ntn888.github.io/tags/gaming-pc/ + + + https://ntn888.github.io/tags/gaming/ + + + https://ntn888.github.io/tags/green-energy/ + + + https://ntn888.github.io/tags/hardware/ + + + https://ntn888.github.io/tags/hosting/ + + + https://ntn888.github.io/tags/i2c/ + + + https://ntn888.github.io/tags/ipv6/ + + + https://ntn888.github.io/tags/jtag/ + + + https://ntn888.github.io/tags/linux-gamimg/ + + + https://ntn888.github.io/tags/llamav2/ + + + https://ntn888.github.io/tags/low-power-wireless/ + + + https://ntn888.github.io/tags/math/ + + + https://ntn888.github.io/tags/matlab/ + + + https://ntn888.github.io/tags/micro-bit/ + + + https://ntn888.github.io/tags/microcontrollers/ + + + https://ntn888.github.io/tags/nas/ + + + https://ntn888.github.io/tags/networking/ + + + https://ntn888.github.io/tags/nrf52/ + + + https://ntn888.github.io/tags/nuclear-energy/ + + + https://ntn888.github.io/tags/nuttx/ + + + https://ntn888.github.io/tags/oled-display/ + + + https://ntn888.github.io/tags/oled/ + + + https://ntn888.github.io/tags/open-source/ + + + https://ntn888.github.io/tags/opensource/ + + + https://ntn888.github.io/tags/pi-pico/ + + + https://ntn888.github.io/tags/pwm/ + + + https://ntn888.github.io/tags/riot-os/ + + + https://ntn888.github.io/tags/risc-v/ + + + https://ntn888.github.io/tags/rtos/ + + + https://ntn888.github.io/tags/sdk/ + + + https://ntn888.github.io/tags/seedbox/ + + + https://ntn888.github.io/tags/self-host/ + + + https://ntn888.github.io/tags/self-reflection/ + + + https://ntn888.github.io/tags/selfhosted/ + + + https://ntn888.github.io/tags/sensors/ + + + https://ntn888.github.io/tags/server/ + + + https://ntn888.github.io/tags/servers/ + + + https://ntn888.github.io/tags/social-media/ + + + https://ntn888.github.io/tags/ssd1306/ + + + https://ntn888.github.io/tags/ssg-frameworks/ + + + https://ntn888.github.io/tags/stm32/ + + + https://ntn888.github.io/tags/supply-chain/ + + + https://ntn888.github.io/tags/swd/ + + + https://ntn888.github.io/tags/text-editor/ + + + https://ntn888.github.io/tags/timers/ + + + https://ntn888.github.io/tags/troubleshooting/ + + + https://ntn888.github.io/tags/uart/ + + + https://ntn888.github.io/tags/vps/ + + + https://ntn888.github.io/tags/welcome/ + + + https://ntn888.github.io/tags/workstation/ + + + https://ntn888.github.io/tags/zigbee/ + + diff --git a/tags/accelerometer/index.html b/tags/accelerometer/index.html new file mode 100644 index 0000000..8a06c7d --- /dev/null +++ b/tags/accelerometer/index.html @@ -0,0 +1,3 @@ +My Blog
# accelerometerAll Tags
Project: Gravity Detector 2023-05-06
\ No newline at end of file diff --git a/tags/ai/index.html b/tags/ai/index.html new file mode 100644 index 0000000..1087d80 --- /dev/null +++ b/tags/ai/index.html @@ -0,0 +1,3 @@ +My Blog
Zephyr-7b-beta: Or how to run a ChatGPT alternative on an 8GB Graphics Card 2023-11-14 Checking out a free opensource ChatGPT alternative: Zephry-7b-Beta 2023-11-12 Mastering the Llama: Unleashing the Power of Self-Hosted AI with Llama GPT 2023-11-10
\ No newline at end of file diff --git a/tags/analog/index.html b/tags/analog/index.html new file mode 100644 index 0000000..207b1a9 --- /dev/null +++ b/tags/analog/index.html @@ -0,0 +1,3 @@ +My Blog
# analogAll Tags
Analog Filters Primer 2022-02-27
\ No newline at end of file diff --git a/tags/assembly/index.html b/tags/assembly/index.html new file mode 100644 index 0000000..e2fd6f0 --- /dev/null +++ b/tags/assembly/index.html @@ -0,0 +1,3 @@ +My Blog
# assemblyAll Tags
C linker scripts howto 2022-11-16
\ No newline at end of file diff --git a/tags/bargain-diy/index.html b/tags/bargain-diy/index.html new file mode 100644 index 0000000..5be4544 --- /dev/null +++ b/tags/bargain-diy/index.html @@ -0,0 +1,3 @@ +My Blog
# bargain diyAll Tags
Introduction to AliExpress for the DIY Enthusiast 2023-11-10
\ No newline at end of file diff --git a/tags/bl702/index.html b/tags/bl702/index.html new file mode 100644 index 0000000..980d84d --- /dev/null +++ b/tags/bl702/index.html @@ -0,0 +1,3 @@ +My Blog
# bl702All Tags
Sipeed BL702 board 2022-12-01 Debugging BL702 2022-03-17 SSD1306 OLED Display 2022-03-17 Board Setup Config 2022-03-15 Device Drivers 101 2022-02-24 freeRTOS Overview 2022-02-24 PWM 2022-02-24 Serial UART 2022-02-24 Timers 2022-02-24 Basic GPIO BL702 2022-02-23 Basic application structure 2022-02-22 Setup a development environment 2022-02-22 THE Dev board you've been waiting for: $1.8 XT-ZB1 Zigbee & BLE devkit features BL702 RISC-V module 2022-02-20
\ No newline at end of file diff --git a/tags/blog/index.html b/tags/blog/index.html new file mode 100644 index 0000000..fb53b0a --- /dev/null +++ b/tags/blog/index.html @@ -0,0 +1,3 @@ +My Blog
# blogAll Tags
Comparison of SSG frameworks 2023-11-05 Review of Zola 2023-11-05 Switched site to Zola SSG 2023-11-03
\ No newline at end of file diff --git a/tags/blue-pill/index.html b/tags/blue-pill/index.html new file mode 100644 index 0000000..a41cb8a --- /dev/null +++ b/tags/blue-pill/index.html @@ -0,0 +1,3 @@ +My Blog
# blue-pillAll Tags
Best microcontroller for Beginners 2023-11-05
\ No newline at end of file diff --git a/tags/debug/index.html b/tags/debug/index.html new file mode 100644 index 0000000..9e8277e --- /dev/null +++ b/tags/debug/index.html @@ -0,0 +1,3 @@ +My Blog
# debugAll Tags
Debugging BL702 2022-03-17 A DIY jtag debugger 2022-02-16
\ No newline at end of file diff --git a/tags/dev-environment/index.html b/tags/dev-environment/index.html new file mode 100644 index 0000000..babd461 --- /dev/null +++ b/tags/dev-environment/index.html @@ -0,0 +1,3 @@ +My Blog
# dev-environmentAll Tags
My experience with Debian Testing 2023-05-11
\ No newline at end of file diff --git a/tags/development/index.html b/tags/development/index.html new file mode 100644 index 0000000..eef0b0a --- /dev/null +++ b/tags/development/index.html @@ -0,0 +1,3 @@ +My Blog
# developmentAll Tags
Basic GPIO BL702 2022-02-23
\ No newline at end of file diff --git a/tags/device-drivers/index.html b/tags/device-drivers/index.html new file mode 100644 index 0000000..2739124 --- /dev/null +++ b/tags/device-drivers/index.html @@ -0,0 +1,3 @@ +My Blog
# device driversAll Tags
Device Drivers 101 2022-02-24
\ No newline at end of file diff --git a/tags/diy/index.html b/tags/diy/index.html new file mode 100644 index 0000000..d29415c --- /dev/null +++ b/tags/diy/index.html @@ -0,0 +1,3 @@ +My Blog
DIY NAS 2023-05-10 A DIY jtag debugger 2022-02-16
\ No newline at end of file diff --git a/tags/documentation/index.html b/tags/documentation/index.html new file mode 100644 index 0000000..e4dd373 --- /dev/null +++ b/tags/documentation/index.html @@ -0,0 +1,3 @@ +My Blog
# documentationAll Tags
Sphinx Documentation Generator 2022-12-07
\ No newline at end of file diff --git a/tags/drop-ship/index.html b/tags/drop-ship/index.html new file mode 100644 index 0000000..3f8f095 --- /dev/null +++ b/tags/drop-ship/index.html @@ -0,0 +1,3 @@ +My Blog
# drop-shipAll Tags
My experience with drop-ship warehousing 2023-05-17
\ No newline at end of file diff --git a/tags/electronic-filter/index.html b/tags/electronic-filter/index.html new file mode 100644 index 0000000..7aa9c1d --- /dev/null +++ b/tags/electronic-filter/index.html @@ -0,0 +1,3 @@ +My Blog
# electronic filterAll Tags
Analog Filters Primer 2022-02-27
\ No newline at end of file diff --git a/tags/electronics-lab/index.html b/tags/electronics-lab/index.html new file mode 100644 index 0000000..0491e14 --- /dev/null +++ b/tags/electronics-lab/index.html @@ -0,0 +1,3 @@ +My Blog
# electronics labAll Tags
Building an affordable electronics lab for hobbyists 2023-11-09 Unveiling the Swiss Army Knife of Embedded Hobbyists: The Multimeter 2023-11-09
\ No newline at end of file diff --git a/tags/electronics/index.html b/tags/electronics/index.html new file mode 100644 index 0000000..74c7d56 --- /dev/null +++ b/tags/electronics/index.html @@ -0,0 +1,3 @@ +My Blog
# electronicsAll Tags
Analog Filters Primer 2022-02-27
\ No newline at end of file diff --git a/tags/emacs/index.html b/tags/emacs/index.html new file mode 100644 index 0000000..d9fe94c --- /dev/null +++ b/tags/emacs/index.html @@ -0,0 +1,3 @@ +My Blog
# emacsAll Tags
DOOM Emacs intro 2022-11-16
\ No newline at end of file diff --git a/tags/embedded-hobbiest/index.html b/tags/embedded-hobbiest/index.html new file mode 100644 index 0000000..9aaa413 --- /dev/null +++ b/tags/embedded-hobbiest/index.html @@ -0,0 +1,3 @@ +My Blog
# embedded hobbiestAll Tags
Review: PI Pico 4MB version 2022-11-04 Switching to the nRFMicro 2022-03-20 Thoughts on the ESP-IDF SDK 2022-03-19 Switching to the ESP32-C3 2022-03-18
\ No newline at end of file diff --git a/tags/embedded-hobbyist/index.html b/tags/embedded-hobbyist/index.html new file mode 100644 index 0000000..877eaf6 --- /dev/null +++ b/tags/embedded-hobbyist/index.html @@ -0,0 +1,3 @@ +My Blog
# embedded hobbyistAll Tags
Introduction to AliExpress for the DIY Enthusiast 2023-11-10 Building an affordable electronics lab for hobbyists 2023-11-09 Unveiling the Swiss Army Knife of Embedded Hobbyists: The Multimeter 2023-11-09 nRF52840 board (Nice!Nano clones) 2023-11-07 Best microcontroller for Beginners 2023-11-05 Micro:Bit IO breakout board 2023-05-05 Low-cost microcontrollers for hobbyists 2022-03-16
\ No newline at end of file diff --git a/tags/energy-production/index.html b/tags/energy-production/index.html new file mode 100644 index 0000000..8ed61fe --- /dev/null +++ b/tags/energy-production/index.html @@ -0,0 +1,3 @@ +My Blog
# energy productionAll Tags
Go nuclear! 2023-05-21
\ No newline at end of file diff --git a/tags/environment/index.html b/tags/environment/index.html new file mode 100644 index 0000000..cab1fb7 --- /dev/null +++ b/tags/environment/index.html @@ -0,0 +1,3 @@ +My Blog
# environmentAll Tags
Board Setup Config 2022-03-15 Basic application structure 2022-02-22 Setup a development environment 2022-02-22
\ No newline at end of file diff --git a/tags/fimware-develompent/index.html b/tags/fimware-develompent/index.html new file mode 100644 index 0000000..84381d5 --- /dev/null +++ b/tags/fimware-develompent/index.html @@ -0,0 +1,3 @@ +My Blog
# fimware develompentAll Tags
Thoughts on the ESP-IDF SDK 2022-03-19 Switching to the ESP32-C3 2022-03-18 Low-cost microcontrollers for hobbyists 2022-03-16
\ No newline at end of file diff --git a/tags/freertos/index.html b/tags/freertos/index.html new file mode 100644 index 0000000..6063fef --- /dev/null +++ b/tags/freertos/index.html @@ -0,0 +1,3 @@ +My Blog
# freeRTOSAll Tags
freeRTOS Overview 2022-02-24
\ No newline at end of file diff --git a/tags/gaming-pc/index.html b/tags/gaming-pc/index.html new file mode 100644 index 0000000..05b15d4 --- /dev/null +++ b/tags/gaming-pc/index.html @@ -0,0 +1,3 @@ +My Blog
# gaming PCAll Tags
Building a Frugal Gaming PC 2023-11-08
\ No newline at end of file diff --git a/tags/gaming/index.html b/tags/gaming/index.html new file mode 100644 index 0000000..5ed473b --- /dev/null +++ b/tags/gaming/index.html @@ -0,0 +1,3 @@ +My Blog
# gamingAll Tags
Lutris: The Linux Gaming Hero 2023-11-09
\ No newline at end of file diff --git a/tags/green-energy/index.html b/tags/green-energy/index.html new file mode 100644 index 0000000..33c8fb7 --- /dev/null +++ b/tags/green-energy/index.html @@ -0,0 +1,3 @@ +My Blog
# green energyAll Tags
Go nuclear! 2023-05-21
\ No newline at end of file diff --git a/tags/hardware/index.html b/tags/hardware/index.html new file mode 100644 index 0000000..03b3230 --- /dev/null +++ b/tags/hardware/index.html @@ -0,0 +1,3 @@ +My Blog
# hardwareAll Tags
Building a Frugal Gaming PC 2023-11-08 Building a Developer's Workstation 2023-05-23
\ No newline at end of file diff --git a/tags/hosting/index.html b/tags/hosting/index.html new file mode 100644 index 0000000..927f30a --- /dev/null +++ b/tags/hosting/index.html @@ -0,0 +1,3 @@ +My Blog
# hostingAll Tags
Comparison of SSG frameworks 2023-11-05 Review of Zola 2023-11-05 Switched site to Zola SSG 2023-11-03
\ No newline at end of file diff --git a/tags/i2c/index.html b/tags/i2c/index.html new file mode 100644 index 0000000..bd4b51e --- /dev/null +++ b/tags/i2c/index.html @@ -0,0 +1,3 @@ +My Blog
SSD1306 OLED Display 2022-03-17
\ No newline at end of file diff --git a/tags/index.html b/tags/index.html new file mode 100644 index 0000000..fa8bc68 --- /dev/null +++ b/tags/index.html @@ -0,0 +1,3 @@ +Tags
Tags
\ No newline at end of file diff --git a/tags/ipv6/index.html b/tags/ipv6/index.html new file mode 100644 index 0000000..bb83851 --- /dev/null +++ b/tags/ipv6/index.html @@ -0,0 +1,3 @@ +My Blog
# IPv6All Tags
Deploying an IPv6 based seedbox 2023-09-16
\ No newline at end of file diff --git a/tags/jtag/index.html b/tags/jtag/index.html new file mode 100644 index 0000000..aa8851d --- /dev/null +++ b/tags/jtag/index.html @@ -0,0 +1,3 @@ +My Blog
# JTAGAll Tags
Debugging BL702 2022-03-17
\ No newline at end of file diff --git a/tags/linux-gamimg/index.html b/tags/linux-gamimg/index.html new file mode 100644 index 0000000..e96d97d --- /dev/null +++ b/tags/linux-gamimg/index.html @@ -0,0 +1,3 @@ +My Blog
# linux gamimgAll Tags
Lutris: The Linux Gaming Hero 2023-11-09
\ No newline at end of file diff --git a/tags/llamav2/index.html b/tags/llamav2/index.html new file mode 100644 index 0000000..26f0a83 --- /dev/null +++ b/tags/llamav2/index.html @@ -0,0 +1,3 @@ +My Blog
# llamav2All Tags
Zephyr-7b-beta: Or how to run a ChatGPT alternative on an 8GB Graphics Card 2023-11-14
\ No newline at end of file diff --git a/tags/low-power-wireless/index.html b/tags/low-power-wireless/index.html new file mode 100644 index 0000000..a711c3d --- /dev/null +++ b/tags/low-power-wireless/index.html @@ -0,0 +1,3 @@ +My Blog
# low-power wirelessAll Tags
802.15.4 Wireless 2023-04-22
\ No newline at end of file diff --git a/tags/math/index.html b/tags/math/index.html new file mode 100644 index 0000000..93bc04e --- /dev/null +++ b/tags/math/index.html @@ -0,0 +1,3 @@ +My Blog
# mathAll Tags
Octave Command Line Mode 2022-02-27
\ No newline at end of file diff --git a/tags/matlab/index.html b/tags/matlab/index.html new file mode 100644 index 0000000..48656ad --- /dev/null +++ b/tags/matlab/index.html @@ -0,0 +1,3 @@ +My Blog
# MATLABAll Tags
Octave Command Line Mode 2022-02-27
\ No newline at end of file diff --git a/tags/micro-bit/index.html b/tags/micro-bit/index.html new file mode 100644 index 0000000..20a1b9e --- /dev/null +++ b/tags/micro-bit/index.html @@ -0,0 +1,3 @@ +My Blog
# micro:bitAll Tags
Micro:Bit IO breakout board 2023-05-05
\ No newline at end of file diff --git a/tags/microcontrollers/index.html b/tags/microcontrollers/index.html new file mode 100644 index 0000000..a8dca7e --- /dev/null +++ b/tags/microcontrollers/index.html @@ -0,0 +1,3 @@ +My Blog
# microcontrollersAll Tags
Best microcontroller for Beginners 2023-11-05 Review: PI Pico 4MB version 2022-11-04 Switching to the nRFMicro 2022-03-20 Thoughts on the ESP-IDF SDK 2022-03-19 Switching to the ESP32-C3 2022-03-18 Low-cost microcontrollers for hobbyists 2022-03-16
\ No newline at end of file diff --git a/tags/nas/index.html b/tags/nas/index.html new file mode 100644 index 0000000..cf30e97 --- /dev/null +++ b/tags/nas/index.html @@ -0,0 +1,3 @@ +My Blog
DIY NAS 2023-05-10
\ No newline at end of file diff --git a/tags/networking/index.html b/tags/networking/index.html new file mode 100644 index 0000000..bc49865 --- /dev/null +++ b/tags/networking/index.html @@ -0,0 +1,3 @@ +My Blog
# networkingAll Tags
Basic commands for Network Troubleshooting 2023-05-16
\ No newline at end of file diff --git a/tags/nrf52/index.html b/tags/nrf52/index.html new file mode 100644 index 0000000..8b491ad --- /dev/null +++ b/tags/nrf52/index.html @@ -0,0 +1,3 @@ +My Blog
# nRF52All Tags
nRF52840 board (Nice!Nano clones) 2023-11-07 Project: Gravity Detector 2023-05-06 Micro:Bit IO breakout board 2023-05-05 802.15.4 Wireless 2023-04-22 Switching to the nRFMicro 2022-03-20
\ No newline at end of file diff --git a/tags/nuclear-energy/index.html b/tags/nuclear-energy/index.html new file mode 100644 index 0000000..e04a7d9 --- /dev/null +++ b/tags/nuclear-energy/index.html @@ -0,0 +1,3 @@ +My Blog
# nuclear energyAll Tags
Go nuclear! 2023-05-21
\ No newline at end of file diff --git a/tags/nuttx/index.html b/tags/nuttx/index.html new file mode 100644 index 0000000..dc4c3d3 --- /dev/null +++ b/tags/nuttx/index.html @@ -0,0 +1,3 @@ +My Blog
# nuttxAll Tags
NuttX Supported Boards 2022-02-18
\ No newline at end of file diff --git a/tags/oled-display/index.html b/tags/oled-display/index.html new file mode 100644 index 0000000..b432188 --- /dev/null +++ b/tags/oled-display/index.html @@ -0,0 +1,3 @@ +My Blog
# oled displayAll Tags
SSD1306 OLED Display 2022-03-17
\ No newline at end of file diff --git a/tags/oled/index.html b/tags/oled/index.html new file mode 100644 index 0000000..7fca55e --- /dev/null +++ b/tags/oled/index.html @@ -0,0 +1,3 @@ +My Blog
# oledAll Tags
First project in RIOT-OS: OLED 2023-11-10
\ No newline at end of file diff --git a/tags/open-source/index.html b/tags/open-source/index.html new file mode 100644 index 0000000..3feb812 --- /dev/null +++ b/tags/open-source/index.html @@ -0,0 +1,3 @@ +My Blog
# open-sourceAll Tags
My experience with Debian Testing 2023-05-11 Selfhosted email delivery with postfix 2022-12-27 Gitlab Selfhohsted! 2022-11-23 DOOM Emacs intro 2022-11-16 Octave Command Line Mode 2022-02-27
\ No newline at end of file diff --git a/tags/opensource/index.html b/tags/opensource/index.html new file mode 100644 index 0000000..910a7c7 --- /dev/null +++ b/tags/opensource/index.html @@ -0,0 +1,3 @@ +My Blog
# opensourceAll Tags
Zephyr-7b-beta: Or how to run a ChatGPT alternative on an 8GB Graphics Card 2023-11-14 Checking out a free opensource ChatGPT alternative: Zephry-7b-Beta 2023-11-12 Mastering the Llama: Unleashing the Power of Self-Hosted AI with Llama GPT 2023-11-10 Lutris: The Linux Gaming Hero 2023-11-09
\ No newline at end of file diff --git a/tags/pi-pico/index.html b/tags/pi-pico/index.html new file mode 100644 index 0000000..08881de --- /dev/null +++ b/tags/pi-pico/index.html @@ -0,0 +1,3 @@ +My Blog
# PI picoAll Tags
Review: PI Pico 4MB version 2022-11-04
\ No newline at end of file diff --git a/tags/pwm/index.html b/tags/pwm/index.html new file mode 100644 index 0000000..82ca3ed --- /dev/null +++ b/tags/pwm/index.html @@ -0,0 +1,3 @@ +My Blog
PWM 2022-02-24
\ No newline at end of file diff --git a/tags/riot-os/index.html b/tags/riot-os/index.html new file mode 100644 index 0000000..8f8ba0f --- /dev/null +++ b/tags/riot-os/index.html @@ -0,0 +1,3 @@ +My Blog
# riot osAll Tags
First project in RIOT-OS: OLED 2023-11-10
\ No newline at end of file diff --git a/tags/risc-v/index.html b/tags/risc-v/index.html new file mode 100644 index 0000000..28d8c36 --- /dev/null +++ b/tags/risc-v/index.html @@ -0,0 +1,3 @@ +My Blog
# risc-vAll Tags
Sipeed BL702 board 2022-12-01 C linker scripts howto 2022-11-16 THE Dev board you've been waiting for: $1.8 XT-ZB1 Zigbee & BLE devkit features BL702 RISC-V module 2022-02-20
\ No newline at end of file diff --git a/tags/rtos/index.html b/tags/rtos/index.html new file mode 100644 index 0000000..1f9c77d --- /dev/null +++ b/tags/rtos/index.html @@ -0,0 +1,3 @@ +My Blog
# rtosAll Tags
NuttX Supported Boards 2022-02-18
\ No newline at end of file diff --git a/tags/sdk/index.html b/tags/sdk/index.html new file mode 100644 index 0000000..70ff5af --- /dev/null +++ b/tags/sdk/index.html @@ -0,0 +1,3 @@ +My Blog
Board Setup Config 2022-03-15 Device Drivers 101 2022-02-24 freeRTOS Overview 2022-02-24 PWM 2022-02-24 Serial UART 2022-02-24 Timers 2022-02-24 Basic GPIO BL702 2022-02-23 Basic application structure 2022-02-22 Setup a development environment 2022-02-22
\ No newline at end of file diff --git a/tags/seedbox/index.html b/tags/seedbox/index.html new file mode 100644 index 0000000..f97f619 --- /dev/null +++ b/tags/seedbox/index.html @@ -0,0 +1,3 @@ +My Blog
# seedboxAll Tags
Piracy on a Budget 2022-11-24
\ No newline at end of file diff --git a/tags/self-host/index.html b/tags/self-host/index.html new file mode 100644 index 0000000..845e8a6 --- /dev/null +++ b/tags/self-host/index.html @@ -0,0 +1,3 @@ +My Blog
# self-hostAll Tags
DIY NAS 2023-05-10
\ No newline at end of file diff --git a/tags/self-reflection/index.html b/tags/self-reflection/index.html new file mode 100644 index 0000000..095dc97 --- /dev/null +++ b/tags/self-reflection/index.html @@ -0,0 +1,3 @@ +My Blog
# self-reflectionAll Tags
Switched site to Zola SSG 2023-11-03
\ No newline at end of file diff --git a/tags/selfhosted/index.html b/tags/selfhosted/index.html new file mode 100644 index 0000000..8db6fe8 --- /dev/null +++ b/tags/selfhosted/index.html @@ -0,0 +1,3 @@ +My Blog
# selfhostedAll Tags
Zephyr-7b-beta: Or how to run a ChatGPT alternative on an 8GB Graphics Card 2023-11-14 Checking out a free opensource ChatGPT alternative: Zephry-7b-Beta 2023-11-12 Mastering the Llama: Unleashing the Power of Self-Hosted AI with Llama GPT 2023-11-10 Selfhosted email delivery with postfix 2022-12-27 Sphinx Documentation Generator 2022-12-07 Piracy on a Budget 2022-11-24 Gitlab Selfhohsted! 2022-11-23 Self hosted Mastodon Instance 2022-11-13
\ No newline at end of file diff --git a/tags/sensors/index.html b/tags/sensors/index.html new file mode 100644 index 0000000..8e69003 --- /dev/null +++ b/tags/sensors/index.html @@ -0,0 +1,3 @@ +My Blog
# sensorsAll Tags
Project: Gravity Detector 2023-05-06
\ No newline at end of file diff --git a/tags/server/index.html b/tags/server/index.html new file mode 100644 index 0000000..7296c00 --- /dev/null +++ b/tags/server/index.html @@ -0,0 +1,3 @@ +My Blog
# serverAll Tags
Deploying an IPv6 based seedbox 2023-09-16
\ No newline at end of file diff --git a/tags/servers/index.html b/tags/servers/index.html new file mode 100644 index 0000000..c2dc64a --- /dev/null +++ b/tags/servers/index.html @@ -0,0 +1,3 @@ +My Blog
# serversAll Tags
Selfhosted email delivery with postfix 2022-12-27 Piracy on a Budget 2022-11-24 Gitlab Selfhohsted! 2022-11-23 Self hosted Mastodon Instance 2022-11-13
\ No newline at end of file diff --git a/tags/social-media/index.html b/tags/social-media/index.html new file mode 100644 index 0000000..9f47d65 --- /dev/null +++ b/tags/social-media/index.html @@ -0,0 +1,3 @@ +My Blog
# social-mediaAll Tags
Self hosted Mastodon Instance 2022-11-13
\ No newline at end of file diff --git a/tags/ssd1306/index.html b/tags/ssd1306/index.html new file mode 100644 index 0000000..d22df13 --- /dev/null +++ b/tags/ssd1306/index.html @@ -0,0 +1,3 @@ +My Blog
# ssd1306All Tags
First project in RIOT-OS: OLED 2023-11-10 SSD1306 OLED Display 2022-03-17
\ No newline at end of file diff --git a/tags/ssg-frameworks/index.html b/tags/ssg-frameworks/index.html new file mode 100644 index 0000000..6b5cc99 --- /dev/null +++ b/tags/ssg-frameworks/index.html @@ -0,0 +1,3 @@ +My Blog
# ssg frameworksAll Tags
Comparison of SSG frameworks 2023-11-05 Review of Zola 2023-11-05
\ No newline at end of file diff --git a/tags/stm32/index.html b/tags/stm32/index.html new file mode 100644 index 0000000..5239943 --- /dev/null +++ b/tags/stm32/index.html @@ -0,0 +1,3 @@ +My Blog
# stm32All Tags
Best microcontroller for Beginners 2023-11-05
\ No newline at end of file diff --git a/tags/supply-chain/index.html b/tags/supply-chain/index.html new file mode 100644 index 0000000..6f5d7c0 --- /dev/null +++ b/tags/supply-chain/index.html @@ -0,0 +1,3 @@ +My Blog
# supply chainAll Tags
My experience with drop-ship warehousing 2023-05-17
\ No newline at end of file diff --git a/tags/swd/index.html b/tags/swd/index.html new file mode 100644 index 0000000..e7ec827 --- /dev/null +++ b/tags/swd/index.html @@ -0,0 +1,3 @@ +My Blog
A DIY jtag debugger 2022-02-16
\ No newline at end of file diff --git a/tags/text-editor/index.html b/tags/text-editor/index.html new file mode 100644 index 0000000..df29875 --- /dev/null +++ b/tags/text-editor/index.html @@ -0,0 +1,3 @@ +My Blog
# text-editorAll Tags
DOOM Emacs intro 2022-11-16
\ No newline at end of file diff --git a/tags/timers/index.html b/tags/timers/index.html new file mode 100644 index 0000000..28fb17f --- /dev/null +++ b/tags/timers/index.html @@ -0,0 +1,3 @@ +My Blog
# timersAll Tags
Timers 2022-02-24
\ No newline at end of file diff --git a/tags/troubleshooting/index.html b/tags/troubleshooting/index.html new file mode 100644 index 0000000..a42d2fc --- /dev/null +++ b/tags/troubleshooting/index.html @@ -0,0 +1,3 @@ +My Blog
# troubleshootingAll Tags
Basic commands for Network Troubleshooting 2023-05-16
\ No newline at end of file diff --git a/tags/uart/index.html b/tags/uart/index.html new file mode 100644 index 0000000..a9f2757 --- /dev/null +++ b/tags/uart/index.html @@ -0,0 +1,3 @@ +My Blog
# UARTAll Tags
Serial UART 2022-02-24
\ No newline at end of file diff --git a/tags/vps/index.html b/tags/vps/index.html new file mode 100644 index 0000000..618bd1e --- /dev/null +++ b/tags/vps/index.html @@ -0,0 +1,3 @@ +My Blog
Deploying an IPv6 based seedbox 2023-09-16
\ No newline at end of file diff --git a/tags/welcome/index.html b/tags/welcome/index.html new file mode 100644 index 0000000..b3594a6 --- /dev/null +++ b/tags/welcome/index.html @@ -0,0 +1,3 @@ +My Blog
# welcomeAll Tags
Welcome 2022-02-16
\ No newline at end of file diff --git a/tags/workstation/index.html b/tags/workstation/index.html new file mode 100644 index 0000000..7483b56 --- /dev/null +++ b/tags/workstation/index.html @@ -0,0 +1,3 @@ +My Blog
# workstationAll Tags
Building a Frugal Gaming PC 2023-11-08 Building a Developer's Workstation 2023-05-23 My experience with Debian Testing 2023-05-11
\ No newline at end of file diff --git a/tags/zigbee/index.html b/tags/zigbee/index.html new file mode 100644 index 0000000..18e20df --- /dev/null +++ b/tags/zigbee/index.html @@ -0,0 +1,3 @@ +My Blog
# zigbeeAll Tags
802.15.4 Wireless 2023-04-22 Sipeed BL702 board 2022-12-01 THE Dev board you've been waiting for: $1.8 XT-ZB1 Zigbee & BLE devkit features BL702 RISC-V module 2022-02-20
\ No newline at end of file