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..5c7df8c --- /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..87b8759 --- /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..38ad261 --- /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..57e2c94 --- /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..f63226b --- /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..f3720f7 --- /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..219cad3 --- /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..fdac757 --- /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..603b57f --- /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/buildroot-devicetree/index.html b/blog/buildroot-devicetree/index.html new file mode 100644 index 0000000..2bbf622 --- /dev/null +++ b/blog/buildroot-devicetree/index.html @@ -0,0 +1,20 @@ +Booting a custom device tree file source in buildroot

Booting a custom device tree file source in buildroot

2023-12-21

In the previous article we saw how to setup the default system image for Orange PI Zero 3 using buildroot. We got the basic system up and running. Now, when we do Device Driver Development, we will have to modify the device tree source.

The default dt source for our board is in the kernel sources in:

arch/arm64/boot/dts/allwinner/sun50i-h616-orangepi-zero3.dts
+

In this exercise we will modify the model name to something different, OrangePi Zero3-custom. To do this we will create a new dts file in the buildroot directory, include in it the original dts file (noted above), and override the model property.

Now create the following file ~/buildroot/sun50i-h616-orangepi-zero3-custom.dts:

// SPDX-License-Identifier: GPL-2.0
+#include "/home/ajit/opi/buildroot/output/build/linux-custom/arch/arm64/boot/dts/allwinner/sun50i-h616-orangepi-zero3.dts"
+/ {
+        model = "OrangePi Zero3-custom";
+        compatible = "xunlong,orangepi-zero3", "allwinner,sun50i-h616";
+
+};
+
+

In menuconfig set the value of BR2_LINUX_KERNEL_CUSTOM_DTS_PATH to ~/buildroot/sun50i-h616-orangepi-zero3-custom.dts our custom dts file location. And unset the BR2_LINUX_KERNEL_INTREE_DTS_NAME.

Once that's done you're ready to do the build. Here we will be doing an incremental build (since we have already built the system earlier).

make linux-rebuild
+

This will (in our case) only compile the dtb file and re-install some modules. To do the sdcard image generation run:

make
+

There is a caveat I noticed with uboot. You now need to set CONFIG_DEFAULT_FDT_FILE in uboot menuconfig to use this custom dtb. But unfortunately our board include files do not honour this setting, and in boot time, uboot proceeds to fetch the default dtb! The work-around is to set the right dtb file on uboot prompt explained below.

Flash the sdcard and boot.

At uboot prompt provide these commands to boot Linux using our custom dtb.

setenv fdtfile sun50i-h616-orangepi-zero3-custom.dtb
+boot
+

This is the workaround that worked for me and in the boot log you can see the custom model that we set: OrangePi Zero3-custom. You may also cat the following file:

cat /sys/firmware/devicetree/base/model
+

If you get any further issues you can also try to manually boot as follows:

setenv bootargs "root=/dev/mmcblk1p1 rootfstype=ext4"
+
+ext4load mmc 0:1 ${kernel_addr_r} boot/Image
+ext4load mmc 0:1 ${fdt_addr_r} boot/sun50i-h616-orangepi-zero3-custom.dtb
+booti ${kernel_addr_r} - ${fdt_addr_r}
+
\ No newline at end of file diff --git a/blog/buildroot-setup/index.html b/blog/buildroot-setup/index.html new file mode 100644 index 0000000..912660c --- /dev/null +++ b/blog/buildroot-setup/index.html @@ -0,0 +1,39 @@ +Set-up a cross-compile environment for buildroot Linux system running on Orange PI Zero 3

Set-up a cross-compile environment for buildroot Linux system running on Orange PI Zero 3

2023-12-19

Assuming host system Ubuntu 22.04. Install the pre-requisite packages needed for this lab:

sudo apt install build-essential git autoconf bison flex texinfo help2man gawk libtool-bin libncurses5-dev unzip
+

Setting up the TFTP server#

Let’s install a TFTP server on your development workstation:

sudo apt install tftpd-hpa
+

Then edit the file /etc/default/tftpd-hpa to change this setting:

TFTP_DIRECTORY="/srv/tftp"
+

Now restart the service:

systemctl restart tftpd-hpa
+

Grab and compile buildroot#

git clone https://git.buildroot.net/buildroot
+cd buildroot
+

Load the default config for Orange PI Zero 3. And build it. Note that we shouldn't change the kernel version as it's been assigned to a custom version compatible with Orange PI.

make orangepi_zero3_defconfig
+make
+

Once the build process is finished you will have an image called "sdcard.img" in the output/images/ directory. Flash this image to your SD card and boot.

On login prompt enter root and no password. TA-DA - and you're in your very own custom linux system!

For more details see Using a build system, example with Buildroot chapter in the slides bootlin embedded linux qemu slides.

Compiling a kernel module#

Now we'll setup the cross-compile variables.

Include the following in your PATH:

export PATH="$PATH:~/buildroot/output/host/bin"
+

And set the following variables:

export ARCH=arm64
+export CROSS_COMPILE=aarch64-linux-
+

Make a new directory somewhere in your home folder called hello. Inside it create a C file hello.c:

#include <linux/module.h>    /* Needed by all modules */
+#include <linux/kernel.h>    /* Needed for KERN_INFO */
+
+int init_module(void){
+    printk(KERN_INFO "Hello World!\n");
+
+    return 0;
+}
+
+void cleanup_module(void){
+    printk(KERN_INFO "Goodbye World.\n");
+}
+
+MODULE_LICENSE("GPL");
+
+

Create Makefile:

CC = $(CROSS_COMPILE)gcc
+
+obj-m := hello.o
+KDIR := ~/buildroot/output/build/linux-custom
+
+all:
+        $(MAKE) -C $(KDIR) M=$(PWD)
+
+

Noting to adjust the KDIR according to where you have buildroot. Assuming Homefolder here.

Now run make. You'll get a bunch of files as result. We are interested in hello.ko file.

Serial console setup#

The board has 3 pins at the top exposing the serial UART. This will act as the serial console to interact with it over the terminal. The following image from the OrangePI website indicates these pins (TX/RX).

OPI Zero3 Pinouts

You may link this to a PC using a ftdi chip (available extensively on AliExpress), or like what I do use a Pico-probe which is equipped with a serial-to-usb port! On the host you can use Screen or Minicom to connect.

Transfer the compiled module#

We need to transfer this file to our board. We'll use the tftp service we setup earlier.

First copy the hello.ko file to /srv/tftp directory. Then in the board's prompt:

cd /root
+tftp -gr hello.ko <server-ip>
+

Once successfully copied, clear the system log, and load the module:

sudo dmesg -c
+insmod hello.ko
+

Now check for the message, running dmesg again. To remove this module, simply rmmod hello.ko. Check the output again and you'll see Goodbye world.

This tests that we have a successful cross-compiler environment working to compile and study device driver development on Orange PI Zero 3 running Linux!

\ 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..88efac5 --- /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..39487a5 --- /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..42de992 --- /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..d239ff1 --- /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..a5a91e3 --- /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..255b78b --- /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/distro-talk/index.html b/blog/distro-talk/index.html new file mode 100644 index 0000000..0fc5d10 --- /dev/null +++ b/blog/distro-talk/index.html @@ -0,0 +1 @@ +My roundup of some popular Linux Distros

My roundup of some popular Linux Distros

2024-01-09

Ubuntu#

I've used Ubuntu for the longest time. I've used it during my formidable learning years, from 2008 to 2018. Whew that was a decade, just thinking about it! I mostly like it. One of the key differentiating factors among distros is their package management. And APT feels robust.

Pop-OS#

My usual go-to distro currently. Been using it since circa 2020. Builds on top of Ubuntu, so you get the niceties of the large user base! Feels like a community distro as opposed to Ubuntu (although I believe it's also a corporate driven one). But it doesn't force snaps down your throat like Ubuntu.

Debian#

The Mother of all distros! Haha! In seriousness, it's the base distro for Ubuntu. And a well know rock-solid one. I generally use it in all my VPS servers whenever I can.

CentOS#

Used to be the most popular server distro until Redhat pulled the cord!

Fedora#

This was my first Linux distro that I started with and briefly used back in 2007. I believe it was called Fedora Core back then. Then I briefly used in 2018/19. During this time, I saw that the package manager was not as stable as Debian's APT, and started giving out errors after a few months of tinkering/use.

I'm currently trialling out daily driving Fedora 39, the latest version as of January 2024. I'm currently learning Linux Kernel module programming, as this will tell how good a developer's distro Fedora is!

Overall Reflection#

I've completely stopped using Windows as my OS back in 2010 and the experience has been a liberating one. One of my favourite aspects about Linux is its hardware compatibility. It sails through on older hardware. Which makes it possible to have setups like these.

Although linux powers majority of the servers; it's heavily lacking in the desktop market-share. Owing to only about 2% usage! Enthusiasts speak about the Year of the Linux desktop. But as much as I wish it were, I don't see it happening anytime soon.

\ 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..8fa8f3f --- /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..8773051 --- /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 and compact, I prefer go with a second hand ex-lease SFF(small form-factor) machine. While this is usually cheaper; you also get a sturdy casing to house your hard drives. They can take a max of 2 storage HDDs. If you want to hold more you can explore other options such as tower PCs. Take a look at NAS Killer builds over at serverbuilds.net.

The used SFF machines come in great discounts on ebay seasonally (as low as $50), get one with 8G of memory and atleast a 4th-gen core-i5 processor. You wouldn't need a powerful machine for you NAS. But if you're streaming your media outside of your local network, plex/emby will have to do transcoding which will take up CPU resource.

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. Here's a post on setting up Radarr quality profiles, so that you pull those juicy, efficient, balanced x265 encodes.

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..0c46b5a --- /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..99827a0 --- /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..113892c --- /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..2dcfd46 --- /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..222bf14 --- /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..c90b94b --- /dev/null +++ b/blog/index.html @@ -0,0 +1 @@ +My Blog
misc
Setup Radarr for x265 encodes 2024-04-27 My roundup of some popular Linux Distros 2024-01-09 NodeMCU: The chip powering countless DIY IoT projects, NodeMCU brings the cloud right onto your board 2023-12-01 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
embedded-linux
Prepare Fedora Linux for Buildroot compilation 2024-01-10 Following the Linux Kernel Module Programming Guide with RISC-V Milkv Duo embedded board 2024-01-06 Learning Embedded Linux Driver Development 2023-12-31 Milk-V Duo: $5 Embedded Linux board 2023-12-27 Booting a custom device tree file source in buildroot 2023-12-21 Set-up a cross-compile environment for buildroot Linux system running on Orange PI Zero 3 2023-12-19
update
Why run local LLMs? 2023-11-30 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
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 PWM 2022-02-24 Timers 2022-02-24 Serial UART 2022-02-24 freeRTOS Overview 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..f3466b5 --- /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-embedded-howto/index.html b/blog/linux-embedded-howto/index.html new file mode 100644 index 0000000..9805084 --- /dev/null +++ b/blog/linux-embedded-howto/index.html @@ -0,0 +1 @@ +Learning Embedded Linux Driver Development

Learning Embedded Linux Driver Development

2023-12-31

There are two kinds of developers that become embedded/driver developers. One set is from a CS / Application developer background that drop down to low-level development. The rest is EE background that turn into developers. Of course both courses have their pros and cons. But I'd argue comming from a Hardware background has it's perks..

Regardless, here's a guide to jumping into Linux driver development from the HW background perspective. Its a collection of resources to guide your learning.

The first step is to tackle general Linux user administration.

  • The Linux Command Line is freely available to download. I think getting familiar with the command line is the first major hurdle in learning Linux. This book helps with that and some BASH scripting too. (I've been putting off learning scripting for the longest time!)

  • How Linux Works is an amazing succinct guide in general Linux admin/usage.

  • Mastering Embedded Linux Programming In contrast to what the title implies (misleading) this book deals with setting up a linux system for an embedded target and discusses the workflow for cross-compilation. And a great one at that! Though this book discusses Yocto I recommend Buildroot for beginners.

  • Linux From Scratch (LFS) is a free guide on how to compile and build a working linux system step-by-step from scratch! Grab that old laptop/machine and follow along! This step is optional but entirely worth it!

Once you have this under your belt you can start development activities.

At this stage you need a good relevant development board to follow along. I recommend the Milk-V Duo for its breadboard friendly. It supports Buildroot and the documentation is highly embedded Linux focussed.

\ 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..bdcff8d --- /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/linux-lkmpg-milkv/index.html b/blog/linux-lkmpg-milkv/index.html new file mode 100644 index 0000000..e8779ba --- /dev/null +++ b/blog/linux-lkmpg-milkv/index.html @@ -0,0 +1,16 @@ +Following the Linux Kernel Module Programming Guide with RISC-V Milkv Duo embedded board

Following the Linux Kernel Module Programming Guide with RISC-V Milkv Duo embedded board

2024-01-06

We saw in this post that The Linux Kernel Module Programming Guide is a great resource when learning Linux Device Drivers. It however takes a self-compiled route with the modules developed and run directly on the host system. For GPIO examples the Raspberry PI is used.

In this article we shall see how to adopt the guide to learn with the Milk-V Duo board. Here we assume you use the vanilla Buildroot repo for building the system image.

Adapting the Makefile#

As mentioned above, the modules are selfcompiled to run locally on the host system. To make them run on our dev board we need to modify the Makefile. The C source file is left untouched.

CC = $(CROSS_COMPILE)gcc
+
+obj-m := hello.o
+KDIR := /home/<user>/milkv/milkv-duo-buildroot/output/build/linux-duo-linux-5.10.4
+
+all:
+	$(MAKE) -C $(KDIR) M=$(PWD)
+
+clean:
+	$(MAKE) -C $(KDIR) M=$$PWD clean
+
+

Be sure to adapt the KDIR path to suite your system's location. Also make note to change hello.o to whatever you named your C source file.

Remember to execute the following environment variables before running make:

export PATH="$PATH:/home/ajit/work/ldd/milkv/milkv-duo-buildroot/output/host/bin"
+
+export ARCH=riscv
+export CROSS_COMPILE=riscv64-linux-
+

Changing the GPIO directives#

In the chapter #detecting-button-presses, they use the Raspberry PI which is based on Cortex-A architecture. Our RISC-V board uses different GPIO naming scheme, and we need to adapt accordingly...

TODO

\ No newline at end of file diff --git a/blog/linux-milkv-duo/index.html b/blog/linux-milkv-duo/index.html new file mode 100644 index 0000000..d98b1b7 --- /dev/null +++ b/blog/linux-milkv-duo/index.html @@ -0,0 +1 @@ +Milk-V Duo: $5 Embedded Linux board

Milk-V Duo: $5 Embedded Linux board

2023-12-27

In my journey of learning Linux Device Drivers, I have since migrated to a new target. I've switched to the Milk-V Duo board. I find that this board has a bigger 'embedded' focused community than the OrangePI Zero3. The forums are fairly active and people occasionally showcase their driver development projects; which I think are a great learning resource too!

Additionally the board is far cheaper, costing just 5usd, and is breadboard friendly.


One caveat with buildroot (we need to use a custom repository) and this board is that the device tree dtb is adopted by the kernel from the uboot sources! ie. There is no separate dts file in the kernel sources. So you must edit the file milkv-duo-buildroot/output/build/uboot-v2021.10_64mb/arch/riscv/dts/cv1800b_milkv_duo_sd.dts (after an initial build) then run make uboot-rebuild and make to include custom device tree modifications. Don't forget to keep a backup of this file somewhere outside buildroot directory as you will lose this file upon make clean.

\ No newline at end of file diff --git a/blog/linux-prepare-fedora/index.html b/blog/linux-prepare-fedora/index.html new file mode 100644 index 0000000..dc5b111 --- /dev/null +++ b/blog/linux-prepare-fedora/index.html @@ -0,0 +1,5 @@ +Prepare Fedora Linux for Buildroot compilation

Prepare Fedora Linux for Buildroot compilation

2024-01-10

Here's a quick note on how to prepare Fedora for compiling Buildroot. The following worked for me for compiling a system for the Milk-V Duo.

First install Fedora's equivalent of build-essential and ncurses (for menuconfig).

sudo dnf group install "C Development Tools and Libraries" "Development Tools"
+
+sudo dnf install ncurses-devel
+

If when running make compilation results in missing Perl modules, install them via:

sudo dnf install 'perl(My::Module)'
+

Fortunately these missing Perl modules error appear at the very beginning of the compilation and causes the least hassle!

\ 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..c025d0b --- /dev/null +++ b/blog/llama-howto/index.html @@ -0,0 +1,30 @@ +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. 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. The following has been exctracted from here.

# install the amdgpu driver with rocm support
+curl -O https://repo.radeon.com/amdgpu-install/5.6/ubuntu/jammy/amdgpu-install_5.6.50600-1_all.deb
+sudo dpkg -i amdgpu-install_5.6.50600-1_all.deb
+

Now edit the install script at /usr/bin/amdgpu-install via a text editor and add |pop next to ubuntu like so:

case "$ID" in
+ubuntu|linuxmint|debian|pop)
+    ...
+
# opencl might cause issues later, so skip it unless you need it
+sudo amdgpu-install --usecase=graphics,rocm --no-dkms
+
+# grant current user the access to gpu devices
+sudo usermod -aG video $USER
+sudo usermod -aG render $USER
+
+# reboot is needed to make both driver and user group take effect
+sudo reboot
+

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
+

The n-gpu-layers is a parameter you get when loading the GGUF models; which can scale between the GPU and CPU as you see fit! So using this parameter you can select, for example, 32 out of the 35 (the max for our zephyr-7b-beta model) to be offloaded to the GPU by selecting 32 here.

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!

But admittedly it is a fast moving landscape. There's new strides being made every single day. And many claim it wouldn't be the distant future that a 7b model easily outperfoms the current performance of GPT-4! And I can't wait for that day to self-host it on my humble 8Gig card.

UPDATE: I installed Ubuntu Server 22.04 on the PC and converted it into a server (and access textgen web ui remotely). Ubuntu being fully supported by AMD; you can omit the --no-dkms flag above to install the kernel modules. With this I've seen a significant improvement in responsiveness. It starts spitting out the text almost instantly (similar to my experiences with vast.ai instances) and get a consistently higher tokens/s. Therefore I do not recommend a POP! OS system.

\ No newline at end of file diff --git a/blog/llama-why-local/index.html b/blog/llama-why-local/index.html new file mode 100644 index 0000000..159282e --- /dev/null +++ b/blog/llama-why-local/index.html @@ -0,0 +1 @@ +Why run local LLMs?

Why run local LLMs?

2023-11-30

Have you ever wished your AI assistant was more flexible? More customized to your specific needs? More under your direct control? As remarkable as large language models like GPT-4 and Bard have been, relying solely on external APIs can leave developers feeling constrained. There's a better way – self-hosting your own private AI.

I distinctly remember the first time I used GPT-4. The sheer power and fluidity of its responses blew my mind. But after the initial wonder faded, limitations began to emerge. Requests for complex code would hit computation limits. Queries using sensitive data made me uneasy. And being dependent on a third-party API limited what customizations I could make.

Friendly chat bot in space

Like Dorothy realizing her magical Oz was being powered by a man behind a curtain, shifting to a self-hosted large language model lifted the veil for me. Finally I had an AI assistant answering to me alone, trained on my data, living on my infrastructure. The flexibility, control, privacy, cost savings, and custom integrations unlocked have been a revelation.

As AI rapidly becomes essential business infrastructure, more teams are arriving at the same revelation. Self-hosted solutions allow you to mold a private AI assistant to your exact needs, without unpredictable costs or outside entanglements. Read on as we explore the benefits, options, and considerations shifting from rented to owned AI. I may be biased, but I believe you too will prefer having your friendly AI wizard in-house.

More Flexibility and Control#

When it comes to leveraging AI, flexibility and control are everything. Relying on rigid external APIs can leave your options limited. By self-hosting a large language model, you open new dimensions of customization to tailor it precisely to your goals.

Fine-Tuning Unlocks Targeted Performance#

Pre-trained models like GPT-4 and Bard display remarkable breadth, but their general knowledge comes at the cost of precision on specialized tasks. Fine-tuning allows you to customize models by continuing to train them on your own data, steering their intelligence toward your specific needs.

Whether improving code suggestions, analyzing scientific papers, or answering domain-specific questions, additional training refines their relevancy and accuracy. On-premise models give you the power over this enhancement process instead of being limited by a third party. The more tailored to your use cases, the better it performs.

Optimizing Compute for Variable Demand#

Self-hosted AI allows you to scale resources fluidly to meet fluctuating needs. Server capacity can expand seamlessly via the cloud to handle spikes in model queries. When demand decreases, reduce resources accordingly. This saves substantially versus paying for peak API capacity at all times.

The ability to load balance queries across devices and data centers further bolsters flexible distribution of compute. You also retain control to upgrade to new hardware quickly instead of relying on a provider’s refresh cycle, keeping model performance on the cutting edge.

Agility to Update, Experiment and Improve#

On-premise models empower your team to rapidly iterate experiments and innovations in AI capabilities. Changes don’t require coordination across organizations or account managers. Want to try enhancing prompts or expanding the training dataset? On your own systems, new ideas can be tested straightaway.

Having your AI engine in-house frees you to build additional tools and custom interfaces tailored to your workflows. As new techniques and model architectures emerge, self-hosted AI means you dictate the integration pace rather than being locked into a rigid vendor roadmap. With great power over your AI comes great product responsibility – are you ready?

Enhanced Privacy and Security#

When relying on external AI systems, privacy and security considerations can quickly spiral into headaches. By retaining direct control over data and models on internal infrastructure, self-hosted LLMs simplify safeguarding information while meeting compliance needs.

Sensitive Data Stays Onsite#

Every industry deals with confidential data, whether customer info, financials, medical history or IP. Transmitting this data externally to utilize AI APIs rightly raises alarms for security and compliance teams. Just because insights from the data have business value doesn't mean IT oversight should be bypassed.

Self-hosted AI eliminates these concerns by keeping all processing onsite. Data remains within your firewall at all times, visible only to approved internal teams. With proper access controls, even admins running the AI systems can be prevented from directly viewing sensitive information used to train models.

Streamlining Regulatory and Policy Compliance#

From financial regulations like GDPR to healthcare rules like HIPAA, evolving legal expectations make safe data handling trickier by the year. By retaining data and AI systems in-house instead of relying on cloud services, the barriers to compliance are dramatically reduced.

Your own infrastructure allows auditing and controls tailored to your exact regulatory needs, with less dependence on third-party attestations. Data residency laws also come into play requiring information stay within national borders, easily addressed via on-premise solutions.

Cutting External Dependencies Improves Security Posture#

Every external API call or cloud service dependency increases vulnerabilities by expanding the corporate attack surface. Just look at the barrage of stabilizer AI incidents last year! Self-hosted models help prevent such headaches by eliminating external connections associated with AI functions.

Isolation also enables creating something like an "air gap" via machinery only used for model handling, disconnected from wider business networks. Though not bulletproof, minimizing touch points via private AI infrastructure pushes security in the right direction.

Lower Long-Term Costs#

When evaluating the financial implications of AI systems, it's essential to take a big picture perspective. Though recognizing ongoing costs, over years self-hosted solutions often prove far more economical than reliance on rental APIs requiring endless subscription fees.

Avoiding Mounting API Expenses#

It's easy to only compare upfront expenses when adopting new technology, failing to account for recurring fees endless draining budgets over time. Leading LLM APIs often run $0.002+ per 1,000 tokens processed. For context, this essay already tallied over 3,400 tokens – costing over $6 at that rate!

While the convenience of instantly available AI is appealing, even moderate enterprise usage adds up to staggering sums. Budget-conscious leaders rightly question chiefly benefiting API shareholder value long-term for functionality becoming a commodity.

Leveraging Existing Infrastructure#

Rather than building from scratch, self-hosted AI can integrate with current on-premise servers and hardware many enterprises already own. Though still representing an investment, extending existing capacity is far cheaper than standalone rental expenses.

Private AI also allows you to dictate upgrade cycles rather than relying on a provider's hardware refresh rate. Regular advances in GPU/TPU processing mean efficiency gains offsetting growing model sizes. In five years the compute powering innovations today could easily fit on a desktop.

Improved Return as Models Compound Gains#

A core advantage of LLMs lies in their ability to build upon prior learning. Over months and years of consistent data exposure, even hosted locally their performances continue improving. This means models become an appreciating asset intrinsically delivering multiplying value beyond static rental APIs.

With customer interactions, new products, and technical advances expanding data pools,Compose even longer form content with deeper analysis the accuracy and quality of outputs compound faster on privately controlled infrastructure. Much like wine aging to perfection in your cellar.

Easier Integration and Customization#

Beyond core model functionality, realizing AI's full potential requires tailored integration with business systems and processes. Self-hosted infrastructure fosters frictionless customization that rented APIs simply can't match.

Streamlining Connections to Internal Data & Apps#

Extracting maximum value from AI necessitates easy interoperability with other stacks powering operations. Self-hosted models co-located on-premise simplify linking to internal databases, analytics tools, CRM and ERP platforms etc. without external touchpoints.

With direct data access, models can programmatically pull the latest info and update training without manual efforts. Code can connect to other apps via API allowing AI to enhance workflows across departments. Avoid integration hassles or changes breaking links to external providers.

Building Custom Interfaces & Experiences#

The client interface heavily impacts perceived AI quality by employees and customers. Rented APIs mean being stuck with vanilla experiences, but self-hosted options empower developing bespoke tools aligned to your brand.

Beyond skins and themes, you can tailor interactions to specific audiences within your organization. Data scientists may prefer Python notebooks while business analysts appreciate no-code web UIs. White label the output for customer facing applications. The sky's limit when controlling both model and interface.

Innovating New Products & Services#

Owning the full AI stack fosters launching entirely new solutions. As examples, AI could personalized customer marketing content, analyze warranty claims and highlight areas for engineering improvements, or bootstrap insurance policy document review.

With the flexibility to evolve models and build around their capabilities, you're only limited by imagination rather than restrictions imposed by external platforms. Build a strategic advantage by doubling down on proprietary efforts rivals can’t replicate relying on vendors.

Conclusion#

As we've explored across critical areas like flexibility, security, costs, and customization, self-hosting your own large language model for private use provides transformational advantages compared to reliance on external AI rental services.

By retaining direct control over your AI assistant within your own infrastructure, you gain unmatched ability to customize to your specific data, workflows, and evolving needs. Keeping processing on-premise together with the sensitive information used for training also slashes compliance risks and data privacy concerns.

And while upfront investment is required, over the long-term self-hosted models can significantly reduce expenses versus open-ended API subscriptions. All while better leverage of existing systems and multiplying accuracy through continual learning compound benefits.

I encourage you to seriously pursue bringing customized AI capabilities in-house. Start small if needed (See this post for inspiration!), but the long-term dividends across security, costs, and performance make owning your AI absolutely worthwhile. Just be careful as it can become highly addictive once tuned to your goals!

The essential next step is evaluating options matching deployment and training requirements, but with the right vision the loops of constant improvement can truly make a private AI assistant feel like your devoted partner in innovation. Here's to a more customized, controlled AI future.

\ No newline at end of file diff --git a/blog/llama/index.html b/blog/llama/index.html new file mode 100644 index 0000000..e2d6ce9 --- /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..1f1b1ee --- /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 wrong1.

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!

1

I've finally managed to run it. See this post for updated info.

\ No newline at end of file diff --git a/blog/mastodon/index.html b/blog/mastodon/index.html new file mode 100644 index 0000000..7eb9faa --- /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..1b1366a --- /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..d0b49b6 --- /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..87b2d86 --- /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

Are you ready to embark on an exciting adventure through the unknown territories of electronic measurement? If so, get your toolkit handy and prepare to experience an unforgettable journey as we dive deep into the mysterious world of multimeters! In this tantalizing guide, we'll explore a myriad of intriguing features, ground-breaking techniques, captivating personal experiences... and, of course, how they all fit together in making electronic measurements fun and hassle-free for both amateur enthusiasts and expert technicians alike. So buckle up, strap on your thinking cap, because it's time to discover the true potential of our multimeter pals!

Using a multimeter

Key Features and Specifications of Multimeters#

Now let's discuss those vital functions you will find on a multimeter: Voltage mode measures AC or DC voltage (in volts) while Resistance (Ohm) measurement provides the basic information about electrical components in a circuit... but there’s more! Did you know that these devices can also measure other interesting parameters like capacitance, frequency, and even determine if two conductive points touch each other, thanks to its inductive range and continuity checker function? Fascinating, right?! ✨ Each feature has its significance - trust us on this one! We encourage you not only to understand their meaning but also how they operate with different types of components and setups.

Understanding the various measurements and features that a multimeter offers goes beyond technical knowledge – it enhances problem-solving abilities as well! Plus, knowing all this adds depth to your hobby/ profession and showcases competency at the workplace (impressive bonus points there!) Remember, these little guys are far more capable than we realize initially. So let's unleash their full potential together!

Types of Multimeters: From Analog to Digital Modes#

Let’s start with our trusty companion - the Analog Multimeter (or as your grandparents called them, 'Old Skool Dudes'). These bad boys operate using needle and dial displays which some purists claim to provide a more analog sense (hmm... we thought they were just being picky). Despite that eccentricity, these veterans offer good readings at an affordable cost. The major drawback is they are relatively less precise due to user calibration error. However, they still remain popular for simple diagnostics and testing tasks.

In contrast stands our high-tech warrior - the Digital Multimeter (AKA 'Smarty Pants Meter'), featuring easy-to-read LCD screens offering exact results! Thanks to its advanced circuitry, these digital beasts provide unmatched accuracy and a plethora of additional features including data storage, auto shutdown, overload protection... need I say more? 😍 It’s almost like having a personal assistant! Remember though, although they have cool bells & whistles, learning how to operate them takes time due to their intricate technology. But hey, who said getting smarter wasn't worth some extra effort?

Safety Measures When Using Multimeters#

We're delving deeper into multimeter safety measures: everything from tips for dealing with live electricity to proper handling techniques and personal protective equipment (PPE) recommendations. Trust us, you don't want to skimp on these crucial aspects! Remember when mamma said 'safety first?!' Well, she was right all along.

When we say live electricity, we mean the type of power source you might find running through electrical wiring in your house or car - let's call it Mr. High-Voltagie. It packs a serious punch and if not handled correctly can lead to some nasty surprises like shocks, fires, or even death (scary, right?). Therefore, always unplug the device you want to measure before connecting your meter... just think of it as polite electricity etiquette. 😄

As for proper handling techniques, one key rule to remember is NEVER TOUCH THE PROBE TIPS! Always touch the metal parts (cause who likes getting zapped anyways?). Another important reminder - never leave the probes connected to the power source while not in use because it could short circuit your equipment. Sounds complex, right? But trust us, with practice and attention, you'll become an expert multimeter handler in no time!

Common Use Cases for Multimeters: Auto Repair, Renewable Energy, & More!#

How about trying self-diagnosis during auto maintenance? I bet that'll impress your mechanic (just don't tell them, though). Multimeters come in handy for troubleshooting engine issues, electrical problems, and so much more. It’s like having a mini toolbox that tells you exactly what's going wrong under the hood!

Another interesting use case? Renewable energy systems (e.g., solar panels) depend heavily on multimeters for maintenance and optimization. Ever thought about working towards saving Mother Earth while mastering new skills at the same time? There you go - your chance!

Let's not forget those living off-grid or experimenters playing with alternative energy sources; trusty old multimeter can be a game-changer here, too. Monitoring power consumption, voltage fluctuation... it becomes so much easier and safer than trying to read your appliances by the light they emit (although I admit, that was a funny experiment!).

Remember folks - every use case brings unique challenges and opportunities; embrace them all, expand your skillset, grow as an electronic problem solver. The sky's the limit when you're armed with such a versatile tool like a multimeter!

Tips for Choosing Your Perfect Multimeter - Features, Cost, Accuracy Matters!#

Firstly, let’s talk about safety measures. Safety glasses on, mateys! If a device can detect voltages from zero to more than sixty volts while also telling you the amount of amps passing through - it should be at the top of your list, trust us. Why? Well, because playing with dangerous current levels without adequate protection is not cool... it's downright reckless. ☠️⏱

Secondly, let’s consider cost factors. Some folks might opt for cheaper alternatives, believing they won't use their multimeters much after initial setup. But guess what? Quality doesn’t come cheap! Spend a bit more on a sturdy tool that offers better reliability over time; it saves money in the long run and peace of mind (totally worth it, right?).

As for accuracy... if your readings don't add up properly, well then things might get messy - think explosive experiments gone awry. Accuracy determines how precise those readings will be! Remember: no margin for error here, chummers! Look out for instruments capable of +/- 0.5% tolerance rating; they provide you with high precision measurements needed during troubleshooting tasks or lab work - a real lifesaver when accuracy is crucial! 🔍💖

And finally, my hearty mates - never underestimate personal preferences! Everyone has their own preferred 'kitchen gear'. You should feel at home using your multimeter; so look for one with user-friendly interface and design tailored to your hand preference or specific needs... after all, happy tools = happy you!

  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.

Final Thoughts - Unlock the Potential of Electronic Measurements!#

Well now, we've reached the end of our marvelous multimeter adventure - how about one last hooray? 🎉✨ This wonderful world of electronic measurement surely holds more secrets waiting to be unraveled by you! With each key feature explored in detail and tips given on choosing the perfect meter for your specific needs, embarking on these explorations with a trusty multimeter is no longer a daunting task. Remember folks, knowing where your voltage or current values stand can save time during troubleshooting or help you understand more about different circuits - truly powerful stuff!

So what’s next? It's simple: put your newfound knowledge into practice. You could start by conducting exciting DIY projects or even just trying to maintain and monitor everyday gadgets at home or work... imagine showing off to friends how precise their phone charger is performing, wouldn’t that be an ego boost?!

Here’s a little nugget of advice: invest in quality multi-meters since they're more durable and offer better reliability over time. After all, good tools inspire creative problem-solving! And who knows? Maybe your name will someday be etched amongst tech legends who changed the world through their brilliant inventions.

Alrighty then adventurers, gear up for even more exciting embedded electronics encounters here on this blog. It's been a blast exploring with you so far and we can't wait to continue this adventure together! Onward to our next exciting chapter - stay safe, experiment, learn!

\ 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..10ebd43 --- /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/nodemcu/index.html b/blog/nodemcu/index.html new file mode 100644 index 0000000..721f166 --- /dev/null +++ b/blog/nodemcu/index.html @@ -0,0 +1 @@ +NodeMCU: The chip powering countless DIY IoT projects, NodeMCU brings the cloud right onto your board

NodeMCU: The chip powering countless DIY IoT projects, NodeMCU brings the cloud right onto your board

2023-12-01

Introduction to NodeMCU#

In the vast and ever-evolving cosmos of the Internet of Things (IoT), there lies a star that has been steadily rising to prominence - the NodeMCU. It's not just a piece of technology; it's a gateway to endless possibilities, a canvas for your creativity. Imagine a world where your ideas aren't just ideas, but tangible, interactive, and intelligent creations. This is the world NodeMCU invites you to explore.

At its core, NodeMCU represents a fusion of dreams and reality for IoT aficionados and DIY hobbyists. It's a testament to how far we've come in the world of embedded systems and how accessible sophisticated technology has become. With its versatile nature and user-friendly approach, NodeMCU is more than just a microcontroller; it's a beacon of innovation in the IoT space.

From its humble beginnings to its current status as a cornerstone of IoT projects, NodeMCU has journeyed through an evolution that mirrors the growth of IoT itself. In this comprehensive guide, we'll embark on an exciting exploration of NodeMCU - uncovering its key features, capabilities, and why it has become an indispensable tool in the hands of creators and innovators.

Whether you are taking your first steps into the world of embedded electronics or you're a seasoned veteran looking to expand your toolkit, NodeMCU offers a world where limitations are set only by your imagination. Let's dive into this journey together, discovering how NodeMCU is not just changing the landscape of IoT, but also empowering us to build, innovate, and dream bigger.

Technical Deep-Dive: Understanding NodeMCU#

Welcome to the heart of our guide, where we unravel the intricacies of NodeMCU. This section is more than just technical jargon; it's a journey into the core of what makes NodeMCU a standout player in the IoT world. As we dissect its components and functionalities, you'll gain a deeper understanding and appreciation for this incredible tool. Let’s dive into the details and uncover the magic behind NodeMCU.

NodeMCU Firmware: Lua and C Programming Language Insights#

NodeMCU is not your ordinary firmware; it's a revolutionary approach to programming microcontrollers. At its core, NodeMCU offers a unique blend of flexibility and power, thanks to its dual compatibility with Lua and C programming languages. Lua, known for its simplicity and efficiency, is an interpreter language embedded within NodeMCU. This means you can write Lua scripts directly for the ESP8266, transforming complex ideas into simple, yet powerful code.

But what if you're more comfortable with C? That's where NodeMCU stands apart. You can use the Arduino IDE, a familiar and beloved tool among developers, to program your ESP8266 with NodeMCU. This dual-language support breaks down barriers, making NodeMCU accessible to a broader range of programmers, from beginners to experts. Whether you're scripting in Lua or coding in C, NodeMCU offers a seamless, user-friendly experience, opening up a world of possibilities in IoT development.

ESP-12E Module and Its Layout#

At the heart of NodeMCU is the ESP-12E module, a powerful and versatile piece of hardware. The ESP-12E is part of the ESP8266 family but stands out with its generous provision of 17 GPIO (General Purpose Input/Output) pins. This abundance of pins opens the door to a myriad of functionalities, from controlling LEDs and sensors to communicating with other devices. But it's not just about quantity; the quality and flexibility of these GPIO pins make the ESP-12E module incredibly efficient for a wide range of IoT applications.

The layout of the ESP-12E module is a testament to its user-friendly design. Featuring edge castellations, it's a breeze to solder onto a PCB, making it ideal for both beginners and seasoned makers. The module also includes an ADC (Analog to Digital Converter) pin and SPI (Serial Peripheral Interface) pins, further enhancing its versatility. Whether you're building a simple home automation system or a complex IoT network, the ESP-12E module, with its robust layout and extensive GPIO options, is perfectly equipped to bring your projects to life.

Pinout and GPIO Pins Explained#

Understanding the pinout of the NodeMCU is crucial for unleashing its full potential. Each pin on the NodeMCU serves a specific function, and knowing how to utilize them effectively can significantly enhance your IoT projects. The GPIO pins, for example, are the heroes of the NodeMCU universe. They allow you to interact with a wide array of sensors and actuators, making them the building blocks of any IoT system.

But the NodeMCU's functionality extends beyond just GPIO pins. It also includes specialized pins for ADC and SPI, allowing for more complex operations like reading analog sensors or communicating with other microcontrollers. This combination of GPIO, ADC, and SPI pins on the NodeMCU makes it a versatile and powerful tool for any IoT enthusiast. Whether you're toggling LEDs, reading sensor data, or sending signals to other devices, the NodeMCU's pinout offers the flexibility and power you need to bring your ideas to life.

This section of the guide aims to provide a detailed and educational insight into the technical aspects of NodeMCU. By understanding these fundamentals, you'll be better equipped to harness the full potential of NodeMCU in your IoT projects.

pinout

Setting Up NodeMCU#

Embarking on the NodeMCU journey begins with setting it up correctly. This section is crucial for anyone looking to harness the full potential of NodeMCU in their projects. Whether you're a beginner or have some experience, these steps will guide you through the initial setup process, ensuring a smooth and successful start to your NodeMCU adventures.

Initial Steps: Installing Arduino IDE and Configuring for NodeMCU#

Getting started with NodeMCU involves a familiar friend for many developers – the Arduino Integrated Development Environment (IDE). The first step is to ensure you have Arduino IDE installed on your computer. This powerful, user-friendly platform is the gateway to programming your NodeMCU.

Once the Arduino IDE is up and running, it’s time to make it NodeMCU-ready. This involves a simple but crucial step: adding the ESP8266 board manager to the Arduino IDE. By navigating to File -> Preferences, and inserting the URL https://arduino.esp8266.com/stable/package_esp8266com_index.json into the “Additional Boards Manager URLs” field, you enable the IDE to support NodeMCU programming. This small step is a giant leap in unlocking the programming capabilities of NodeMCU with the comfort and familiarity of Arduino IDE.

Connecting and Programming NodeMCU with Arduino IDE#

With the Arduino IDE configured, the next step is to connect your NodeMCU to your computer using a micro-USB cable. This simple connection is where the magic begins, turning your computer into a command center for NodeMCU. Once connected, you'll select the NodeMCU board under Tools -> Board -> ESP8266 Boards, ensuring that the IDE knows exactly what hardware it's communicating with.

Programming the NodeMCU via Arduino IDE is an exhilarating experience. You can write or upload sketches just like you would for an Arduino board. The process involves selecting the right port under Tools -> Port, where your NodeMCU is connected. After writing your code or loading an existing sketch, hitting the upload button brings your code to life on the NodeMCU. It’s a seamless transition from code on screen to action in the real world, bridging the gap between imagination and reality.

Common Issues and Troubleshooting#

As with any technology, you might encounter some hiccups along the way. Common issues with setting up NodeMCU often include problems with port detection, driver compatibility, or board selection in Arduino IDE. Troubleshooting these problems typically involves ensuring that the correct drivers for the USB to UART bridge (like CP2102) are installed, and that the correct board and port are selected in the Arduino IDE.

Another common issue is the failure of the computer to recognize the NodeMCU board. This can usually be resolved by trying a different USB cable or port, as some cables are charge-only and do not support data transfer. Additionally, ensuring that your NodeMCU board is not physically damaged and that the USB to UART bridge is functioning correctly can help solve connectivity issues. Remember, a little bit of troubleshooting can go a long way in ensuring a smooth experience with NodeMCU.

By following these detailed steps and being aware of common issues, you will be well on your way to successfully setting up your NodeMCU. This setup is your first step into a world where your creative IoT projects come to life.

Exploring NodeMCU Components#

As we delve deeper into the NodeMCU ecosystem, it becomes essential to understand the components that make up this versatile board. This section is dedicated to unraveling the mysteries of the NodeMCU's hardware, providing you with the knowledge needed to fully leverage its capabilities. From its onboard controller to its unique features, every component plays a vital role in the functionality and flexibility of NodeMCU.

Breakout Board Features: USB to UART Controller and On-board Regulators#

One of the standout features of the NodeMCU is its onboard USB to UART (Universal Asynchronous Receiver/Transmitter) controller. This component is a game-changer, simplifying the process of uploading code from your computer to the NodeMCU. In many models, the CP2102 IC from Silicon Labs serves as this controller, offering a stable and reliable bridge between your computer's USB port and the NodeMCU's serial interface. This integration means you don't need additional hardware to program your NodeMCU, making it more accessible and user-friendly, especially for beginners.

Alongside the USB to UART controller, the NodeMCU also includes on-board voltage regulators. These regulators are crucial because the ESP8266 chip, which is at the heart of NodeMCU, operates at 3.3V, whereas most computer USB ports supply 5V. The on-board regulator steps down this voltage to a safe level, ensuring that your NodeMCU operates in a stable and secure environment. This feature not only protects the board but also simplifies the power supply, eliminating the need for external voltage regulation.

Understanding LEDs and GPIO Pins#

The NodeMCU board is equipped with two on-board LEDs, which are invaluable for basic diagnostics and for use in your projects. The first LED, typically connected to the GPIO 2 pin, can be programmed to indicate various statuses or actions, such as signaling the completion of a task or the receipt of data. The second LED, usually connected to GPIO 16, offers additional options for feedback and interaction in your projects. These LEDs are active LOW, meaning they turn on when their corresponding GPIO pins are set to a LOW voltage level. This feature allows you to easily test and debug your NodeMCU applications, providing visual feedback directly from the board.

The General Purpose Input/Output (GPIO) pins are the bread and butter of the NodeMCU. These pins are what make the NodeMCU so versatile, allowing it to interact with a wide range of sensors, actuators, and other devices. With a generous number of GPIO pins available, you can connect multiple components simultaneously, enabling complex and multifaceted IoT projects. Each GPIO pin can be programmed for various functions, such as digital input, digital output, PWM (Pulse Width Modulation) output, and more. This flexibility opens up a world of possibilities, allowing your NodeMCU to truly become the heart of your IoT ecosystem.

By gaining a thorough understanding of these components, you're now equipped to make the most out of your NodeMCU board. Whether it's programming the LEDs for basic output or utilizing the GPIO pins for intricate projects, these insights form the foundation of your journey into the fascinating world of NodeMCU and IoT.

Programming NodeMCU#

Programming is where your NodeMCU projects come to life, transforming lines of code into real-world actions and reactions. This section is dedicated to guiding you through the programming aspects of NodeMCU, from writing your first script to exploring advanced programming techniques. Whether you are a beginner or an experienced developer, understanding how to effectively program the NodeMCU is key to unlocking its full potential.

Writing and Uploading Basic Scripts (e.g., Blinking LED Example)#

A great starting point for programming the NodeMCU is the classic 'Blinking LED' example. This simple project is not just about turning an LED on and off; it's a fundamental step in understanding how to control the NodeMCU's GPIO pins through programming. To begin, you'll write a script in the Arduino IDE that toggles one of the onboard LEDs. This script will use basic commands to set the GPIO pin as an output, and then to turn the LED on (set the pin LOW) and off (set the pin HIGH) in a loop.

Uploading this script to your NodeMCU is straightforward. With your NodeMCU connected to your computer via a micro-USB cable and the correct board and port selected in the Arduino IDE, simply click the 'Upload' button. The IDE compiles the script into bytecode, which is then uploaded to the NodeMCU. Once uploaded, the NodeMCU executes the script, and you should see the LED start to blink. This process not only demonstrates the basics of NodeMCU programming but also familiarizes you with the upload procedure and troubleshooting any issues that may arise, such as connection errors or syntax mistakes in your code.

Lua vs. Arduino IDE: Pros and Cons#

NodeMCU offers the flexibility to program in Lua directly or use the Arduino IDE. Each method has its advantages and considerations. Lua, being a lightweight scripting language, is ideal for simple, standalone NodeMCU projects. It allows for quick development and iteration, with scripts being easier to write and modify. However, Lua might have limitations when it comes to more complex applications, especially those requiring extensive libraries and support that are readily available in the Arduino environment.

On the other hand, programming the NodeMCU using the Arduino IDE opens up a familiar environment for those who have worked with Arduino boards. It provides access to a vast library of existing code and community support, making it easier to implement complex functionalities. While the Arduino IDE offers more robust capabilities, it can be slightly more cumbersome for quick, small-scale projects compared to the straightforwardness of Lua scripting. Ultimately, the choice between Lua and Arduino IDE programming depends on your project requirements and personal preferences.

Advanced Programming Tips and Tricks#

As you become more comfortable with basic NodeMCU programming, you can explore advanced techniques to enhance your projects. This includes mastering the use of interrupts for responsive designs, leveraging WiFi capabilities for IoT connectivity, and optimizing power consumption for battery-operated devices. Understanding how to efficiently manage memory and utilize deep sleep modes can significantly improve the performance and durability of your NodeMCU projects.

Additionally, integrating external sensors and modules expands the functionality of your NodeMCU board. Learning how to interface with different types of sensors (like temperature, humidity, motion sensors) and communication modules (like Bluetooth or Zigbee) opens up a world of possibilities for your IoT projects. Experimenting with these advanced concepts not only broadens the scope of your projects but also deepens your understanding and skills in the realm of embedded systems and IoT.

Through this comprehensive overview of programming NodeMCU, you are now equipped with the knowledge to start creating, experimenting, and innovating. Whether it's writing simple scripts or diving into complex IoT applications, the world of NodeMCU programming is rich with opportunities for exploration and creativity.

Real-World Applications and Projects#

Exploring the practical applications of NodeMCU is where the true excitement lies for IoT enthusiasts and hobbyists. This section aims to bridge the gap between theory and real-world application, showcasing how NodeMCU can be utilized in various projects. From simple home automation to complex IoT systems, NodeMCU’s versatility makes it an ideal choice for a wide range of applications.

Sample IoT Projects using NodeMCU#

Starting with home automation, a popular application for NodeMCU, you can create projects that control lights, thermostats, or even security systems through the internet. For instance, using NodeMCU, you can develop a system that lets you control your home lighting via a smartphone app. This involves programming the NodeMCU to connect to your Wi-Fi network and receive commands from your phone, which then triggers the connected lights through relay modules. Such a project not only introduces you to the basics of IoT but also provides practical, everyday utility.

Another exciting application is creating a weather station. By connecting various sensors to NodeMCU, such as temperature, humidity, and pressure sensors, you can collect real-time environmental data. This data can be sent to an IoT platform like ThingSpeak or your own server, allowing you to monitor and analyze weather patterns over time. This project not only enhances your understanding of sensor integration but also offers insights into data handling and cloud connectivity in IoT systems.

Community Contributions and Case Studies#

The NodeMCU community is vast and active, with members constantly sharing innovative projects and ideas. Engaging with this community can provide invaluable insights and inspiration for your own projects. For example, many community members have shared their experiences in building smart irrigation systems using NodeMCU, which automatically water plants based on soil moisture levels. These case studies often include detailed instructions, code snippets, and troubleshooting tips, making them a great resource for learning and experimentation.

Another area where community contributions shine is in the development of custom IoT applications, such as pet feeders, smart mirrors, or personalized notification systems. These projects often combine various technologies like web development, database management, and mobile app development with NodeMCU, showcasing its ability to integrate seamlessly into broader technology ecosystems. By exploring these case studies, you not only learn about NodeMCU but also about how it interacts with other technologies to create comprehensive solutions.

This exploration of real-world applications and community projects highlights the practicality and adaptability of NodeMCU in a variety of settings. Whether you are looking to enhance your home, understand the environment, or connect with a global community of innovators, NodeMCU offers a platform to turn your ideas into reality. Through these applications, you'll see just how your skills in programming and electronics can have a tangible impact in the world.

NodeMCU: Beyond the Basics#

After mastering the fundamentals of NodeMCU, it's time to push the boundaries and explore its advanced capabilities. This section delves into the more sophisticated aspects of NodeMCU, highlighting how it can be integrated with other technologies and platforms. By expanding your NodeMCU knowledge, you can take your projects from simple applications to complex, interconnected systems that represent the cutting edge of IoT development.

Advanced Modules and Add-ons#

One of the ways to enhance the capabilities of your NodeMCU is by integrating it with various modules and add-ons. For instance, adding a GPS module can transform your NodeMCU project into a location-aware device, perfect for tracking applications or smart vehicles. When integrating a GPS module, you'll delve into serial communication and parsing GPS data, expanding your technical skills and understanding of geolocation technologies.

Another exciting add-on is the incorporation of wireless communication modules, such as Zigbee or Bluetooth Low Energy (BLE). These modules enable your NodeMCU to communicate with other devices wirelessly, opening up possibilities for creating mesh networks, smart home systems, or even IoT hubs. Learning to integrate these modules involves understanding wireless communication protocols and network topology, further broadening your IoT expertise.

Integrating NodeMCU with Other IoT Platforms#

NodeMCU's true potential is unleashed when it's integrated with other IoT platforms. For example, connecting NodeMCU with platforms like AWS IoT or Google Cloud IoT enables you to store, process, and analyze the data collected by your NodeMCU device. This integration requires an understanding of cloud services, data security, and network management, providing an excellent opportunity to delve into the world of cloud computing and big data.

Another integration avenue is using NodeMCU with home automation platforms like Home Assistant or OpenHAB. This integration allows you to create sophisticated home automation systems that are customizable and can interact with a wide range of devices. It involves learning about APIs, MQTT protocol, and smart home standards, offering a comprehensive look into the world of smart home technology.

As IoT continues to evolve, so does NodeMCU. Staying abreast of the latest trends and developments in NodeMCU technology is crucial for any IoT enthusiast. This includes keeping an eye on advancements in firmware, new modules, and emerging IoT protocols that could enhance the capabilities of NodeMCU.

Additionally, the growing focus on AI and machine learning in IoT presents exciting possibilities for NodeMCU projects. Integrating AI algorithms with NodeMCU, for applications like predictive maintenance or environmental monitoring, represents the next frontier in IoT development. This requires a foundational understanding of machine learning concepts and the ability to implement these algorithms in a resource-constrained environment like NodeMCU.

In this section, we've explored the advanced aspects of NodeMCU, from integrating with other technologies to staying updated with future trends. These insights are intended to inspire you to take your NodeMCU projects to new heights, leveraging its full potential to create innovative and impactful IoT solutions.


As we conclude our comprehensive journey through the world of NodeMCU, let's reflect on the rich tapestry of opportunities this versatile tool offers to IoT enthusiasts and hobbyists. From the initial exploration of its technical makeup and firmware options in Section II, where we delved into the intricacies of Lua and C programming, to the practical setup guidance in Section III, ensuring a smooth start to your NodeMCU experience. We journeyed through the crucial hardware components in Section IV, highlighting the significance of onboard controllers and GPIO pins for your projects.

Our adventure then took us into the realms of programming in Section V, where we learned to breathe life into our NodeMCU boards through scripts and advanced programming techniques. Section VI expanded our horizons further, showcasing real-world applications and community-driven projects, illustrating how NodeMCU acts as a cornerstone in various innovative IoT systems. Finally, in Section VII, we ventured beyond the basics to explore advanced modules, integrations with IoT platforms, and emerging trends, positioning NodeMCU at the forefront of IoT development.

Now, armed with this knowledge, the path lies open for you to embark on your own NodeMCU projects. Whether it's automating your home, building a personal weather station, or integrating advanced IoT technologies, the potential is limitless. Remember, the NodeMCU is not just a tool; it's a canvas for your creativity and a bridge to realizing your IoT dreams.

So, take that first step, experiment with a simple blinking LED project, or dive into more complex integrations. Each project, no matter the size, is a step forward in your IoT journey. And as you progress, stay connected with the vibrant NodeMCU community, drawing inspiration and sharing your experiences. Your journey with NodeMCU is just beginning, and the future is bright with possibilities. Embrace it with enthusiasm, and let your imagination lead the way in this exciting world of IoT and embedded systems.

\ 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..44b64b3 --- /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..41d3f63 --- /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..c77d913 --- /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..a0634ee --- /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..7ef8ab4 --- /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..ce0f16d --- /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..e290bfa --- /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..0f6f8b0 --- /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..bd7f9d2 --- /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/radarr-profiles/index.html b/blog/radarr-profiles/index.html new file mode 100644 index 0000000..fae720d --- /dev/null +++ b/blog/radarr-profiles/index.html @@ -0,0 +1 @@ +Setup Radarr for x265 encodes

Setup Radarr for x265 encodes

2024-04-27

This article is a continuation of this post on setting up a DIY NAS. Here we will setup Radarr/Sonarr custom formats, such that it prioritises the efficient x265 1080p encodes.

First go to Settings->Custom Formats and setup two custom formats like so:

RalphyXD-like

Lama-like

With the appropriate conditions as shown. The x265 & x264 are preset conditions available in the drop-down!

Next go to Settings->Profiles. Select HD - 720p/1080p And add the custom formats as indicated below, using the score value to prioritise the x265.

720p/1080p quality profile

Third and final step, setup Quality Definitions. We are interested in 720p & 1080p profiles here.

Quality Slider

Then go ahead Auto 'Search Movie' feature on top in the movie listing page to test it. Be sure to select the HD - 720p/1080p Quality Profile when you add the new movie!

\ 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..daf0f77 --- /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..1c25a1e --- /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..5a84474 --- /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..d9c3836 --- /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..8568b11 --- /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..4d59f1f --- /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..fd43e14 --- /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..56a607f --- /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..1e81d2b --- /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..9161121 --- /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..81bc7ac --- /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. See UPDATE below.

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


UPDATE (31/12/23): On second thought, actually getting the parts separately instead of the MB/CPU/RAM Combo is a better idea. Thanks to the information available on https://xeon-e5450.ru/.

First search Ali for x99 and pick for a good (popular) motherboard. Then head to xeon-e5450.ru and see for compatible RAM. They provide links for matching RAM. If available, going with server RAM is safer and cheaper! And finally check for compatible CPU. I believe the 2011-3 chip Xeon E5 2670v3 is always matched.

Using the above information I have since upgraded my RAM to 32GB ECC Server version and now use the machine as a Linux kernel compilation cruncher! It compliles the full kernel for an embedded system typically under 10mins.

Highly Recommended!

\ 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..8472433 --- /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..805ab01 --- /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..ae847e4 --- /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/embedded-linux/index.html b/categories/embedded-linux/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..3807624 --- /dev/null +++ b/feed.xml @@ -0,0 +1,3710 @@ + + + Ajit Ananthadevan + Interested in everything Embedded. + + + 2024-04-27T17:27:00+00:00 + https://ntn888.github.io/feed.xml + + Setup Radarr for x265 encodes + 2024-04-27T17:27:00+00:00 + 2024-04-27T17:27:00+00:00 + + https://ntn888.github.io/blog/radarr-profiles/ + <p>This article is a continuation of <a href="https://ntn888.github.io/blog/diy-nas/">this post</a> on setting up a DIY NAS. Here we will setup Radarr/Sonarr custom formats, such that it prioritises the efficient x265 1080p encodes.</p> +<p>First go to Settings-&gt;Custom Formats and setup two custom formats like so:</p> +<p><img src="/img/RalphyXD-like.png" alt="RalphyXD-like" /></p> +<p><img src="/img/Lama-like.png" alt="Lama-like" /></p> +<p>With the appropriate conditions as shown. The x265 &amp; x264 are preset conditions available in the drop-down!</p> +<p>Next go to Settings-&gt;Profiles. Select <code>HD - 720p/1080p</code> And add the custom formats as indicated below, using the <code>score</code> value to prioritise the x265.</p> +<p><img src="/img/720p_1080p-quality-profile.png" alt="720p/1080p quality profile" /></p> +<p>Third and final step, setup Quality Definitions. We are interested in 720p &amp; 1080p profiles here.</p> +<p><img src="/img/quality-slider.png" alt="Quality Slider" /></p> +<p>Then go ahead Auto 'Search Movie' feature on top in the movie listing page to test it. Be sure to select the <code>HD - 720p/1080p</code> Quality Profile when you add the new movie!</p> + + + + Prepare Fedora Linux for Buildroot compilation + 2024-01-10T07:27:00+00:00 + 2024-01-10T07:27:00+00:00 + + https://ntn888.github.io/blog/linux-prepare-fedora/ + <p>Here's a quick note on how to prepare Fedora for compiling Buildroot. The following worked for me for compiling a system for the <a href="https://ntn888.github.io/blog/linux-milkv-duo/">Milk-V Duo</a>.</p> +<p>First install Fedora's equivalent of <code>build-essential</code> and ncurses (for menuconfig).</p> +<pre class="z-code"><code><span class="z-text z-plain">sudo dnf group install &quot;C Development Tools and Libraries&quot; &quot;Development Tools&quot; + +sudo dnf install ncurses-devel +</span></code></pre> +<p>If when running <code>make</code> compilation results in missing Perl modules, install them via:</p> +<pre class="z-code"><code><span class="z-text z-plain">sudo dnf install &#39;perl(My::Module)&#39; +</span></code></pre> +<p>Fortunately these missing Perl modules error appear at the very beginning of the compilation and causes the least hassle!</p> + + + + My roundup of some popular Linux Distros + 2024-01-09T12:27:00+00:00 + 2024-01-09T12:27:00+00:00 + + https://ntn888.github.io/blog/distro-talk/ + <h1 id="ubuntu">Ubuntu<a class="zola-anchor" href="#ubuntu" aria-label="Anchor link for: ubuntu">#</a></h1> +<p>I've used Ubuntu for the longest time. I've used it during my formidable learning years, from 2008 to 2018. Whew that was a decade, just thinking about it! I mostly like it. One of the key differentiating factors among distros is their package management. And APT feels robust.</p> +<h1 id="pop-os">Pop-OS<a class="zola-anchor" href="#pop-os" aria-label="Anchor link for: pop-os">#</a></h1> +<p>My usual go-to distro currently. Been using it since circa 2020. Builds on top of Ubuntu, so you get the niceties of the large user base! Feels like a community distro as opposed to Ubuntu (although I believe it's also a corporate driven one). But it doesn't force snaps down your throat like Ubuntu.</p> +<h1 id="debian">Debian<a class="zola-anchor" href="#debian" aria-label="Anchor link for: debian">#</a></h1> +<p>The Mother of all distros! Haha! In seriousness, it's the base distro for Ubuntu. And a well know rock-solid one. I generally use it in all my VPS servers whenever I can.</p> +<h1 id="centos">CentOS<a class="zola-anchor" href="#centos" aria-label="Anchor link for: centos">#</a></h1> +<p>Used to be the most popular server distro until Redhat pulled the cord!</p> +<h1 id="fedora">Fedora<a class="zola-anchor" href="#fedora" aria-label="Anchor link for: fedora">#</a></h1> +<p>This was my first Linux distro that I started with and briefly used back in 2007. I believe it was called Fedora Core back then. Then I briefly used in 2018/19. During this time, I saw that the package manager was not as stable as Debian's APT, and started giving out errors after a few months of tinkering/use.</p> +<p>I'm currently trialling out daily driving Fedora 39, the latest version as of January 2024. I'm currently learning Linux Kernel module programming, as this will tell how good a developer's distro Fedora is!</p> +<h1 id="overall-reflection">Overall Reflection<a class="zola-anchor" href="#overall-reflection" aria-label="Anchor link for: overall-reflection">#</a></h1> +<p>I've completely stopped using Windows as my OS back in 2010 and the experience has been a liberating one. One of my favourite aspects about Linux is its hardware compatibility. It sails through on older hardware. Which makes it possible to have setups like <a href="https://ntn888.github.io/blog/dev-station/">these</a>.</p> +<p>Although linux powers majority of the servers; it's heavily lacking in the desktop market-share. Owing to only about 2% usage! Enthusiasts speak about the <em>Year of the Linux desktop</em>. But as much as I wish it were, I don't see it happening anytime soon.</p> + + + + Following the Linux Kernel Module Programming Guide with RISC-V Milkv Duo embedded board + 2024-01-06T13:27:00+00:00 + 2024-01-06T13:27:00+00:00 + + https://ntn888.github.io/blog/linux-lkmpg-milkv/ + <p>We saw in <a href="https://ntn888.github.io/blog/linux-embedded-howto/">this post</a> that <a rel="nofollow noreferrer" href="https://sysprog21.github.io/lkmpg/">The Linux Kernel Module Programming Guide</a> is a great resource when learning Linux Device Drivers. It however takes a self-compiled route with the modules developed and run directly on the host system. For GPIO examples the Raspberry PI is used.</p> +<p>In this article we shall see how to adopt the guide to learn with the <a rel="nofollow noreferrer" href="https://milkv.io/duo">Milk-V Duo</a> board. Here we assume you use the vanilla Buildroot <a rel="nofollow noreferrer" href="https://github.com/milkv-duo/milkv-duo-buildroot">repo</a> for building the system image.</p> +<h2 id="adapting-the-makefile">Adapting the Makefile<a class="zola-anchor" href="#adapting-the-makefile" aria-label="Anchor link for: adapting-the-makefile">#</a></h2> +<p>As mentioned above, the modules are selfcompiled to run locally on the host system. To make them run on our dev board we need to modify the Makefile. The C source file is left untouched.</p> +<pre data-lang="Makefile" class="language-Makefile z-code"><code class="language-Makefile" data-lang="Makefile"><span class="z-source z-makefile"><span class="z-variable z-other z-makefile">CC</span> <span class="z-keyword z-operator z-assignment z-makefile">=</span></span> <span class="z-source z-makefile"><span class="z-meta z-string z-makefile"><span class="z-string z-unquoted z-makefile"><span class="z-variable z-parameter z-makefile"><span class="z-keyword z-other z-block z-begin z-makefile">$(</span>CROSS_COMPILE<span class="z-keyword z-other z-block z-end z-makefile">)</span></span>gcc</span></span> + +<span class="z-variable z-other z-makefile">obj-m</span> <span class="z-keyword z-operator z-assignment z-makefile">:=</span></span> <span class="z-source z-makefile"><span class="z-meta z-string z-makefile"><span class="z-string z-unquoted z-makefile">hello.o</span></span> +<span class="z-variable z-other z-makefile">KDIR</span> <span class="z-keyword z-operator z-assignment z-makefile">:=</span></span> <span class="z-source z-makefile"><span class="z-meta z-string z-makefile"><span class="z-string z-unquoted z-makefile">/home/&lt;user&gt;/milkv/milkv-duo-buildroot/output/build/linux-duo-linux-5.10.4</span></span> + +<span class="z-meta z-function z-makefile"><span class="z-entity z-name z-function z-makefile">all</span></span><span class="z-keyword z-operator z-assignment z-makefile">:</span> +<span class="z-meta z-function z-arguments z-makefile"></span><span class="z-meta z-function z-body z-makefile"></span><span class="z-meta z-function z-body z-makefile"> <span class="z-source z-shell"><span class="z-variable z-parameter z-makefile"><span class="z-keyword z-other z-block z-begin z-makefile">$(</span>MAKE<span class="z-keyword z-other z-block z-end z-makefile">)</span></span> <span class="z-meta z-function-call z-shell"><span class="z-variable z-function z-shell">-C</span></span><span class="z-meta z-function-call z-arguments z-shell"> <span class="z-variable z-parameter z-makefile"><span class="z-keyword z-other z-block z-begin z-makefile">$(</span>KDIR<span class="z-keyword z-other z-block z-end z-makefile">)</span></span> M=<span class="z-variable z-parameter z-makefile"><span class="z-keyword z-other z-block z-begin z-makefile">$(</span>PWD<span class="z-keyword z-other z-block z-end z-makefile">)</span></span></span></span> + +</span><span class="z-meta z-function z-makefile"><span class="z-entity z-name z-function z-makefile">clean</span></span><span class="z-keyword z-operator z-assignment z-makefile">:</span> +<span class="z-meta z-function z-arguments z-makefile"></span><span class="z-meta z-function z-body z-makefile"></span><span class="z-meta z-function z-body z-makefile"> <span class="z-source z-shell"><span class="z-variable z-parameter z-makefile"><span class="z-keyword z-other z-block z-begin z-makefile">$(</span>MAKE<span class="z-keyword z-other z-block z-end z-makefile">)</span></span> <span class="z-meta z-function-call z-shell"><span class="z-variable z-function z-shell">-C</span></span><span class="z-meta z-function-call z-arguments z-shell"> <span class="z-variable z-parameter z-makefile"><span class="z-keyword z-other z-block z-begin z-makefile">$(</span>KDIR<span class="z-keyword z-other z-block z-end z-makefile">)</span></span> M=<span class="z-constant z-character z-escape z-makefile">$$</span>PWD clean</span></span> + +</span></span></code></pre> +<p>Be sure to adapt the <code>KDIR</code> path to suite your system's location. Also make note to change <code>hello.o</code> to whatever you named your C source file.</p> +<p>Remember to execute the following environment variables before running make:</p> +<pre class="z-code"><code><span class="z-text z-plain">export PATH=&quot;$PATH:/home/ajit/work/ldd/milkv/milkv-duo-buildroot/output/host/bin&quot; + +export ARCH=riscv +export CROSS_COMPILE=riscv64-linux- +</span></code></pre> +<h2 id="changing-the-gpio-directives">Changing the GPIO directives<a class="zola-anchor" href="#changing-the-gpio-directives" aria-label="Anchor link for: changing-the-gpio-directives">#</a></h2> +<p>In the chapter <a rel="nofollow noreferrer" href="https://sysprog21.github.io/lkmpg/#detecting-button-presses">#detecting-button-presses</a>, they use the Raspberry PI which is based on Cortex-A architecture. Our RISC-V board uses different GPIO naming scheme, and we need to adapt accordingly...</p> +<p>TODO</p> + + + + Learning Embedded Linux Driver Development + 2023-12-31T03:27:00+00:00 + 2023-12-31T03:27:00+00:00 + + https://ntn888.github.io/blog/linux-embedded-howto/ + <p>There are two kinds of developers that become embedded/driver developers. One set is from a CS / Application developer background that drop down to low-level development. The rest is EE background that turn into developers. Of course both courses have their pros and cons. But I'd argue comming from a Hardware background has it's perks..</p> +<p>Regardless, here's a guide to jumping into Linux driver development from the HW background perspective. Its a collection of resources to guide your learning.</p> +<p>The first step is to tackle general Linux user administration.</p> +<ul> +<li> +<p><a rel="nofollow noreferrer" href="https://linuxcommand.org/tlcl.php">The Linux Command Line</a> is freely available to download. I think getting familiar with the command line is the first major hurdle in learning Linux. This book helps with that and some BASH scripting too. (I've been putting off learning scripting for the longest time!)</p> +</li> +<li> +<p><a rel="nofollow noreferrer" href="https://www.amazon.com/How-Linux-Works-Brian-Ward/dp/1718500408">How Linux Works</a> is an amazing succinct guide in general Linux admin/usage.</p> +</li> +<li> +<p><a rel="nofollow noreferrer" href="https://www.amazon.com/Mastering-Embedded-Linux-Programming-potential/dp/1789530385">Mastering Embedded Linux Programming</a> In contrast to what the title implies (misleading) this book deals with setting up a linux system for an embedded target and discusses the workflow for cross-compilation. And a great one at that! Though this book discusses Yocto I recommend Buildroot for beginners.</p> +</li> +<li> +<p><a rel="nofollow noreferrer" href="https://www.linuxfromscratch.org/">Linux From Scratch (LFS)</a> is a free guide on how to compile and build a working linux system step-by-step from scratch! Grab that old laptop/machine and follow along! This step is optional but entirely worth it!</p> +</li> +</ul> +<p>Once you have this under your belt you can start development activities.</p> +<ul> +<li> +<p><a rel="nofollow noreferrer" href="https://www.amazon.com/Linux-Programming-Interface-System-Handbook/dp/1593272200">The Linux Programming Interface</a> Learn POSIX application API here. It's a large book; skim on what's needed. I recommend learning atleast <em>pthreads</em>.</p> +</li> +<li> +<p><a rel="nofollow noreferrer" href="https://www.amazon.com/Linux-Device-Driver-Development-development/dp/1803240067">Linux Device Driver Development</a> The crux of our study. Pair it up with <a rel="nofollow noreferrer" href="https://sysprog21.github.io/lkmpg/">The Linux Kernel Module Programming Guide</a>.</p> +</li> +</ul> +<p>At this stage you need a good relevant development board to follow along. I recommend the <a href="https://ntn888.github.io/blog/linux-milkv-duo/">Milk-V Duo</a> for its breadboard friendly. It supports Buildroot and the documentation is highly embedded Linux focussed.</p> + + + + Milk-V Duo: $5 Embedded Linux board + 2023-12-27T15:27:00+00:00 + 2023-12-27T15:27:00+00:00 + + https://ntn888.github.io/blog/linux-milkv-duo/ + <p>In my journey of learning Linux Device Drivers, I have since migrated to a new target. I've switched to the <a rel="nofollow noreferrer" href="https://milkv.io/duo">Milk-V Duo</a> board. I find that this board has a bigger 'embedded' focused community than the OrangePI Zero3. The <a rel="nofollow noreferrer" href="https://community.milkv.io/">forums</a> are fairly active and people occasionally showcase their driver development projects; which I think are a great learning resource too!</p> +<p>Additionally the board is far cheaper, costing just 5usd, and is breadboard friendly.</p> +<hr /> +<p>One caveat with buildroot (we need to use a custom <a rel="nofollow noreferrer" href="https://github.com/milkv-duo/milkv-duo-buildroot">repository</a>) and this board is that the device tree dtb is adopted by the kernel from the uboot sources! ie. There is no separate dts file in the kernel sources. So you must edit the file <code>milkv-duo-buildroot/output/build/uboot-v2021.10_64mb/arch/riscv/dts/cv1800b_milkv_duo_sd.dts</code> (after an initial build) then run <code>make uboot-rebuild</code> and <code>make</code> to include custom device tree modifications. Don't forget to keep a backup of this file somewhere outside buildroot directory as you will lose this file upon <code>make clean</code>.</p> + + + + Booting a custom device tree file source in buildroot + 2023-12-21T15:27:00+00:00 + 2023-12-21T15:27:00+00:00 + + https://ntn888.github.io/blog/buildroot-devicetree/ + <p>In the <a href="https://ntn888.github.io/blog/buildroot-setup/">previous article</a> we saw how to setup the default system image for Orange PI Zero 3 using buildroot. We got the basic system up and running. Now, when we do Device Driver Development, we will have to modify the device tree source.</p> +<p>The default dt source for our board is in the kernel sources in:</p> +<pre class="z-code"><code><span class="z-text z-plain">arch/arm64/boot/dts/allwinner/sun50i-h616-orangepi-zero3.dts +</span></code></pre> +<p>In this exercise we will modify the model name to something different, <code>OrangePi Zero3-custom</code>. To do this we will create a new dts file in the buildroot directory, include in it the original dts file (noted above), and override the <code>model</code> property.</p> +<p>Now create the following file <code>~/buildroot/sun50i-h616-orangepi-zero3-custom.dts</code>:</p> +<pre class="z-code"><code><span class="z-text z-plain">// SPDX-License-Identifier: GPL-2.0 +#include &quot;/home/ajit/opi/buildroot/output/build/linux-custom/arch/arm64/boot/dts/allwinner/sun50i-h616-orangepi-zero3.dts&quot; +/ { + model = &quot;OrangePi Zero3-custom&quot;; + compatible = &quot;xunlong,orangepi-zero3&quot;, &quot;allwinner,sun50i-h616&quot;; + +}; + +</span></code></pre> +<p>In menuconfig set the value of <code>BR2_LINUX_KERNEL_CUSTOM_DTS_PATH</code> to <code>~/buildroot/sun50i-h616-orangepi-zero3-custom.dts</code> our custom dts file location. And unset the <code>BR2_LINUX_KERNEL_INTREE_DTS_NAME</code>.</p> +<p>Once that's done you're ready to do the build. Here we will be doing an incremental build (since we have already built the system earlier).</p> +<pre class="z-code"><code><span class="z-text z-plain">make linux-rebuild +</span></code></pre> +<p>This will (in our case) only compile the dtb file and re-install some modules. To do the sdcard image generation run:</p> +<pre class="z-code"><code><span class="z-text z-plain">make +</span></code></pre> +<blockquote> +<p>There is a caveat I noticed with uboot. You now need to set <code>CONFIG_DEFAULT_FDT_FILE</code> in uboot menuconfig to use this custom dtb. But unfortunately our board include files do not honour this setting, and in boot time, uboot proceeds to fetch the default dtb! The work-around is to set the right dtb file on uboot prompt explained below.</p> +</blockquote> +<p>Flash the sdcard and boot.</p> +<p>At uboot prompt provide these commands to boot Linux using our custom dtb.</p> +<pre class="z-code"><code><span class="z-text z-plain">setenv fdtfile sun50i-h616-orangepi-zero3-custom.dtb +boot +</span></code></pre> +<p>This is the workaround that worked for me and in the boot log you can see the custom model that we set: <code>OrangePi Zero3-custom</code>. You may also cat the following file:</p> +<pre class="z-code"><code><span class="z-text z-plain">cat /sys/firmware/devicetree/base/model +</span></code></pre> +<p>If you get any further issues you can also try to manually boot as follows:</p> +<pre class="z-code"><code><span class="z-text z-plain">setenv bootargs &quot;root=/dev/mmcblk1p1 rootfstype=ext4&quot; + +ext4load mmc 0:1 ${kernel_addr_r} boot/Image +ext4load mmc 0:1 ${fdt_addr_r} boot/sun50i-h616-orangepi-zero3-custom.dtb +booti ${kernel_addr_r} - ${fdt_addr_r} +</span></code></pre> + + + + Set-up a cross-compile environment for buildroot Linux system running on Orange PI Zero 3 + 2023-12-19T00:27:00+00:00 + 2023-12-19T00:27:00+00:00 + + https://ntn888.github.io/blog/buildroot-setup/ + <p>Assuming host system Ubuntu 22.04. Install the pre-requisite packages needed for this lab:</p> +<pre class="z-code"><code><span class="z-text z-plain">sudo apt install build-essential git autoconf bison flex texinfo help2man gawk libtool-bin libncurses5-dev unzip +</span></code></pre> +<h3 id="setting-up-the-tftp-server">Setting up the TFTP server<a class="zola-anchor" href="#setting-up-the-tftp-server" aria-label="Anchor link for: setting-up-the-tftp-server">#</a></h3> +<p>Let’s install a TFTP server on your development workstation:</p> +<pre class="z-code"><code><span class="z-text z-plain">sudo apt install tftpd-hpa +</span></code></pre> +<p>Then edit the file <code>/etc/default/tftpd-hpa</code> to change this setting:</p> +<pre class="z-code"><code><span class="z-text z-plain">TFTP_DIRECTORY=&quot;/srv/tftp&quot; +</span></code></pre> +<p>Now restart the service:</p> +<pre class="z-code"><code><span class="z-text z-plain">systemctl restart tftpd-hpa +</span></code></pre> +<h2 id="grab-and-compile-buildroot">Grab and compile buildroot<a class="zola-anchor" href="#grab-and-compile-buildroot" aria-label="Anchor link for: grab-and-compile-buildroot">#</a></h2> +<pre class="z-code"><code><span class="z-text z-plain">git clone https://git.buildroot.net/buildroot +cd buildroot +</span></code></pre> +<p>Load the default config for Orange PI Zero 3. And build it. Note that we shouldn't change the kernel version as it's been assigned to a custom version compatible with Orange PI.</p> +<pre class="z-code"><code><span class="z-text z-plain">make orangepi_zero3_defconfig +make +</span></code></pre> +<p>Once the build process is finished you will have an image called &quot;sdcard.img&quot; in the output/images/ directory. Flash this image to your SD card and boot.</p> +<p>On login prompt enter <code>root</code> and no password. TA-DA - and you're in your very own custom linux system!</p> +<p>For more details see <em>Using a build system, example with Buildroot</em> chapter in the slides <a rel="nofollow noreferrer" href="https://bootlin.com/doc/training/embedded-linux-qemu/embedded-linux-qemu-labs.pdf">bootlin embedded linux qemu slides</a>.</p> +<h2 id="compiling-a-kernel-module">Compiling a kernel module<a class="zola-anchor" href="#compiling-a-kernel-module" aria-label="Anchor link for: compiling-a-kernel-module">#</a></h2> +<p>Now we'll setup the cross-compile variables.</p> +<p>Include the following in your PATH:</p> +<pre class="z-code"><code><span class="z-text z-plain">export PATH=&quot;$PATH:~/buildroot/output/host/bin&quot; +</span></code></pre> +<p>And set the following variables:</p> +<pre class="z-code"><code><span class="z-text z-plain">export ARCH=arm64 +export CROSS_COMPILE=aarch64-linux- +</span></code></pre> +<p>Make a new directory somewhere in your home folder called <code>hello</code>. Inside it create a C file <code>hello.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>linux/module.h<span class="z-punctuation z-definition z-string z-end z-c">&gt;</span></span> <span class="z-comment z-block z-c"><span class="z-punctuation z-definition z-comment z-c">/*</span> Needed by all modules <span class="z-punctuation z-definition z-comment z-c">*/</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>linux/kernel.h<span class="z-punctuation z-definition z-string z-end z-c">&gt;</span></span> <span class="z-comment z-block z-c"><span class="z-punctuation z-definition z-comment z-c">/*</span> Needed for KERN_INFO <span class="z-punctuation z-definition z-comment 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">init_module</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 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-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">KERN_INFO <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 World!<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></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">void</span> <span class="z-meta z-function z-c"><span class="z-entity z-name z-function z-c">cleanup_module</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 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-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">KERN_INFO <span class="z-string z-quoted z-double z-c"><span class="z-punctuation z-definition z-string z-begin z-c">&quot;</span>Goodbye World.<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></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-function-call z-c"><span class="z-variable z-function z-c">MODULE_LICENSE</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>GPL<span class="z-punctuation z-definition z-string z-end z-c">&quot;</span></span></span><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></code></pre> +<p>Create <code>Makefile</code>:</p> +<pre class="z-code"><code><span class="z-text z-plain">CC = $(CROSS_COMPILE)gcc + +obj-m := hello.o +KDIR := ~/buildroot/output/build/linux-custom + +all: + $(MAKE) -C $(KDIR) M=$(PWD) + +</span></code></pre> +<p>Noting to adjust the <code>KDIR</code> according to where you have buildroot. Assuming Homefolder here.</p> +<p>Now run <code>make</code>. You'll get a bunch of files as result. We are interested in <code>hello.ko</code> file.</p> +<h2 id="serial-console-setup">Serial console setup<a class="zola-anchor" href="#serial-console-setup" aria-label="Anchor link for: serial-console-setup">#</a></h2> +<p>The board has 3 pins at the top exposing the serial UART. This will act as the serial console to interact with it over the terminal. The following image from the OrangePI website indicates these pins (TX/RX).</p> +<p><img src="/img/opi-zero3.png" alt="OPI Zero3 Pinouts" /></p> +<p>You may link this to a PC using a ftdi chip (available extensively on AliExpress), or like what I do use a Pico-probe which is equipped with a serial-to-usb port! On the host you can use Screen or Minicom to connect.</p> +<h3 id="transfer-the-compiled-module">Transfer the compiled module<a class="zola-anchor" href="#transfer-the-compiled-module" aria-label="Anchor link for: transfer-the-compiled-module">#</a></h3> +<p>We need to transfer this file to our board. We'll use the tftp service we setup earlier.</p> +<p>First copy the <code>hello.ko</code> file to <code>/srv/tftp</code> directory. Then in the board's prompt:</p> +<pre class="z-code"><code><span class="z-text z-plain">cd /root +tftp -gr hello.ko &lt;server-ip&gt; +</span></code></pre> +<p>Once successfully copied, clear the system log, and load the module:</p> +<pre class="z-code"><code><span class="z-text z-plain">sudo dmesg -c +insmod hello.ko +</span></code></pre> +<p>Now check for the message, running <code>dmesg</code> again. To remove this module, simply <code>rmmod hello.ko</code>. Check the output again and you'll see Goodbye world.</p> +<p>This tests that we have a successful cross-compiler environment working to compile and study device driver development on Orange PI Zero 3 running Linux!</p> + + + + NodeMCU: The chip powering countless DIY IoT projects, NodeMCU brings the cloud right onto your board + 2023-12-01T00:27:00+00:00 + 2023-12-01T00:27:00+00:00 + + https://ntn888.github.io/blog/nodemcu/ + <h2 id="introduction-to-nodemcu">Introduction to NodeMCU<a class="zola-anchor" href="#introduction-to-nodemcu" aria-label="Anchor link for: introduction-to-nodemcu">#</a></h2> +<p>In the vast and ever-evolving cosmos of the Internet of Things (IoT), there lies a star that has been steadily rising to prominence - the NodeMCU. It's not just a piece of technology; it's a gateway to endless possibilities, a canvas for your creativity. Imagine a world where your ideas aren't just ideas, but tangible, interactive, and intelligent creations. This is the world NodeMCU invites you to explore.</p> +<p>At its core, NodeMCU represents a fusion of dreams and reality for IoT aficionados and DIY hobbyists. It's a testament to how far we've come in the world of embedded systems and how accessible sophisticated technology has become. With its versatile nature and user-friendly approach, NodeMCU is more than just a microcontroller; it's a beacon of innovation in the IoT space.</p> +<p>From its humble beginnings to its current status as a cornerstone of IoT projects, NodeMCU has journeyed through an evolution that mirrors the growth of IoT itself. In this comprehensive guide, we'll embark on an exciting exploration of NodeMCU - uncovering its key features, capabilities, and why it has become an indispensable tool in the hands of creators and innovators.</p> +<p>Whether you are taking your first steps into the world of embedded electronics or you're a seasoned veteran looking to expand your toolkit, NodeMCU offers a world where limitations are set only by your imagination. Let's dive into this journey together, discovering how NodeMCU is not just changing the landscape of IoT, but also empowering us to build, innovate, and dream bigger.</p> +<h2 id="technical-deep-dive-understanding-nodemcu">Technical Deep-Dive: Understanding NodeMCU<a class="zola-anchor" href="#technical-deep-dive-understanding-nodemcu" aria-label="Anchor link for: technical-deep-dive-understanding-nodemcu">#</a></h2> +<p>Welcome to the heart of our guide, where we unravel the intricacies of NodeMCU. This section is more than just technical jargon; it's a journey into the core of what makes NodeMCU a standout player in the IoT world. As we dissect its components and functionalities, you'll gain a deeper understanding and appreciation for this incredible tool. Let’s dive into the details and uncover the magic behind NodeMCU.</p> +<h3 id="nodemcu-firmware-lua-and-c-programming-language-insights">NodeMCU Firmware: Lua and C Programming Language Insights<a class="zola-anchor" href="#nodemcu-firmware-lua-and-c-programming-language-insights" aria-label="Anchor link for: nodemcu-firmware-lua-and-c-programming-language-insights">#</a></h3> +<p>NodeMCU is not your ordinary firmware; it's a revolutionary approach to programming microcontrollers. At its core, NodeMCU offers a unique blend of flexibility and power, thanks to its dual compatibility with Lua and C programming languages. Lua, known for its simplicity and efficiency, is an interpreter language embedded within NodeMCU. This means you can write Lua scripts directly for the ESP8266, transforming complex ideas into simple, yet powerful code.</p> +<p>But what if you're more comfortable with C? That's where NodeMCU stands apart. You can use the Arduino IDE, a familiar and beloved tool among developers, to program your ESP8266 with NodeMCU. This dual-language support breaks down barriers, making NodeMCU accessible to a broader range of programmers, from beginners to experts. Whether you're scripting in Lua or coding in C, NodeMCU offers a seamless, user-friendly experience, opening up a world of possibilities in IoT development.</p> +<h3 id="esp-12e-module-and-its-layout">ESP-12E Module and Its Layout<a class="zola-anchor" href="#esp-12e-module-and-its-layout" aria-label="Anchor link for: esp-12e-module-and-its-layout">#</a></h3> +<p>At the heart of NodeMCU is the ESP-12E module, a powerful and versatile piece of hardware. The ESP-12E is part of the ESP8266 family but stands out with its generous provision of 17 GPIO (General Purpose Input/Output) pins. This abundance of pins opens the door to a myriad of functionalities, from controlling LEDs and sensors to communicating with other devices. But it's not just about quantity; the quality and flexibility of these GPIO pins make the ESP-12E module incredibly efficient for a wide range of IoT applications.</p> +<p>The layout of the ESP-12E module is a testament to its user-friendly design. Featuring edge castellations, it's a breeze to solder onto a PCB, making it ideal for both beginners and seasoned makers. The module also includes an ADC (Analog to Digital Converter) pin and SPI (Serial Peripheral Interface) pins, further enhancing its versatility. Whether you're building a simple home automation system or a complex IoT network, the ESP-12E module, with its robust layout and extensive GPIO options, is perfectly equipped to bring your projects to life.</p> +<h3 id="pinout-and-gpio-pins-explained">Pinout and GPIO Pins Explained<a class="zola-anchor" href="#pinout-and-gpio-pins-explained" aria-label="Anchor link for: pinout-and-gpio-pins-explained">#</a></h3> +<p>Understanding the pinout of the NodeMCU is crucial for unleashing its full potential. Each pin on the NodeMCU serves a specific function, and knowing how to utilize them effectively can significantly enhance your IoT projects. The GPIO pins, for example, are the heroes of the NodeMCU universe. They allow you to interact with a wide array of sensors and actuators, making them the building blocks of any IoT system.</p> +<p>But the NodeMCU's functionality extends beyond just GPIO pins. It also includes specialized pins for ADC and SPI, allowing for more complex operations like reading analog sensors or communicating with other microcontrollers. This combination of GPIO, ADC, and SPI pins on the NodeMCU makes it a versatile and powerful tool for any IoT enthusiast. Whether you're toggling LEDs, reading sensor data, or sending signals to other devices, the NodeMCU's pinout offers the flexibility and power you need to bring your ideas to life.</p> +<p>This section of the guide aims to provide a detailed and educational insight into the technical aspects of NodeMCU. By understanding these fundamentals, you'll be better equipped to harness the full potential of NodeMCU in your IoT projects.</p> +<p><img src="/img/ESP8266-NodeMCU-kit-12-E-pinout-gpio-pin.webp" alt="pinout" /></p> +<h2 id="setting-up-nodemcu">Setting Up NodeMCU<a class="zola-anchor" href="#setting-up-nodemcu" aria-label="Anchor link for: setting-up-nodemcu">#</a></h2> +<p>Embarking on the NodeMCU journey begins with setting it up correctly. This section is crucial for anyone looking to harness the full potential of NodeMCU in their projects. Whether you're a beginner or have some experience, these steps will guide you through the initial setup process, ensuring a smooth and successful start to your NodeMCU adventures.</p> +<h3 id="initial-steps-installing-arduino-ide-and-configuring-for-nodemcu">Initial Steps: Installing Arduino IDE and Configuring for NodeMCU<a class="zola-anchor" href="#initial-steps-installing-arduino-ide-and-configuring-for-nodemcu" aria-label="Anchor link for: initial-steps-installing-arduino-ide-and-configuring-for-nodemcu">#</a></h3> +<p>Getting started with NodeMCU involves a familiar friend for many developers – the Arduino Integrated Development Environment (IDE). The first step is to ensure you have Arduino IDE installed on your computer. This powerful, user-friendly platform is the gateway to programming your NodeMCU.</p> +<p>Once the Arduino IDE is up and running, it’s time to make it NodeMCU-ready. This involves a simple but crucial step: adding the ESP8266 board manager to the Arduino IDE. By navigating to File -&gt; Preferences, and inserting the URL <code>https://arduino.esp8266.com/stable/package_esp8266com_index.json</code> into the “Additional Boards Manager URLs” field, you enable the IDE to support NodeMCU programming. This small step is a giant leap in unlocking the programming capabilities of NodeMCU with the comfort and familiarity of Arduino IDE.</p> +<h3 id="connecting-and-programming-nodemcu-with-arduino-ide">Connecting and Programming NodeMCU with Arduino IDE<a class="zola-anchor" href="#connecting-and-programming-nodemcu-with-arduino-ide" aria-label="Anchor link for: connecting-and-programming-nodemcu-with-arduino-ide">#</a></h3> +<p>With the Arduino IDE configured, the next step is to connect your NodeMCU to your computer using a micro-USB cable. This simple connection is where the magic begins, turning your computer into a command center for NodeMCU. Once connected, you'll select the NodeMCU board under Tools -&gt; Board -&gt; ESP8266 Boards, ensuring that the IDE knows exactly what hardware it's communicating with.</p> +<p>Programming the NodeMCU via Arduino IDE is an exhilarating experience. You can write or upload sketches just like you would for an Arduino board. The process involves selecting the right port under Tools -&gt; Port, where your NodeMCU is connected. After writing your code or loading an existing sketch, hitting the upload button brings your code to life on the NodeMCU. It’s a seamless transition from code on screen to action in the real world, bridging the gap between imagination and reality.</p> +<h3 id="common-issues-and-troubleshooting">Common Issues and Troubleshooting<a class="zola-anchor" href="#common-issues-and-troubleshooting" aria-label="Anchor link for: common-issues-and-troubleshooting">#</a></h3> +<p>As with any technology, you might encounter some hiccups along the way. Common issues with setting up NodeMCU often include problems with port detection, driver compatibility, or board selection in Arduino IDE. Troubleshooting these problems typically involves ensuring that the correct drivers for the USB to UART bridge (like CP2102) are installed, and that the correct board and port are selected in the Arduino IDE.</p> +<p>Another common issue is the failure of the computer to recognize the NodeMCU board. This can usually be resolved by trying a different USB cable or port, as some cables are charge-only and do not support data transfer. Additionally, ensuring that your NodeMCU board is not physically damaged and that the USB to UART bridge is functioning correctly can help solve connectivity issues. Remember, a little bit of troubleshooting can go a long way in ensuring a smooth experience with NodeMCU.</p> +<p>By following these detailed steps and being aware of common issues, you will be well on your way to successfully setting up your NodeMCU. This setup is your first step into a world where your creative IoT projects come to life.</p> +<h2 id="exploring-nodemcu-components">Exploring NodeMCU Components<a class="zola-anchor" href="#exploring-nodemcu-components" aria-label="Anchor link for: exploring-nodemcu-components">#</a></h2> +<p>As we delve deeper into the NodeMCU ecosystem, it becomes essential to understand the components that make up this versatile board. This section is dedicated to unraveling the mysteries of the NodeMCU's hardware, providing you with the knowledge needed to fully leverage its capabilities. From its onboard controller to its unique features, every component plays a vital role in the functionality and flexibility of NodeMCU.</p> +<h3 id="breakout-board-features-usb-to-uart-controller-and-on-board-regulators">Breakout Board Features: USB to UART Controller and On-board Regulators<a class="zola-anchor" href="#breakout-board-features-usb-to-uart-controller-and-on-board-regulators" aria-label="Anchor link for: breakout-board-features-usb-to-uart-controller-and-on-board-regulators">#</a></h3> +<p>One of the standout features of the NodeMCU is its onboard USB to UART (Universal Asynchronous Receiver/Transmitter) controller. This component is a game-changer, simplifying the process of uploading code from your computer to the NodeMCU. In many models, the CP2102 IC from Silicon Labs serves as this controller, offering a stable and reliable bridge between your computer's USB port and the NodeMCU's serial interface. This integration means you don't need additional hardware to program your NodeMCU, making it more accessible and user-friendly, especially for beginners.</p> +<p>Alongside the USB to UART controller, the NodeMCU also includes on-board voltage regulators. These regulators are crucial because the ESP8266 chip, which is at the heart of NodeMCU, operates at 3.3V, whereas most computer USB ports supply 5V. The on-board regulator steps down this voltage to a safe level, ensuring that your NodeMCU operates in a stable and secure environment. This feature not only protects the board but also simplifies the power supply, eliminating the need for external voltage regulation.</p> +<h3 id="understanding-leds-and-gpio-pins">Understanding LEDs and GPIO Pins<a class="zola-anchor" href="#understanding-leds-and-gpio-pins" aria-label="Anchor link for: understanding-leds-and-gpio-pins">#</a></h3> +<p>The NodeMCU board is equipped with two on-board LEDs, which are invaluable for basic diagnostics and for use in your projects. The first LED, typically connected to the GPIO 2 pin, can be programmed to indicate various statuses or actions, such as signaling the completion of a task or the receipt of data. The second LED, usually connected to GPIO 16, offers additional options for feedback and interaction in your projects. These LEDs are active LOW, meaning they turn on when their corresponding GPIO pins are set to a LOW voltage level. This feature allows you to easily test and debug your NodeMCU applications, providing visual feedback directly from the board.</p> +<p>The General Purpose Input/Output (GPIO) pins are the bread and butter of the NodeMCU. These pins are what make the NodeMCU so versatile, allowing it to interact with a wide range of sensors, actuators, and other devices. With a generous number of GPIO pins available, you can connect multiple components simultaneously, enabling complex and multifaceted IoT projects. Each GPIO pin can be programmed for various functions, such as digital input, digital output, PWM (Pulse Width Modulation) output, and more. This flexibility opens up a world of possibilities, allowing your NodeMCU to truly become the heart of your IoT ecosystem.</p> +<p>By gaining a thorough understanding of these components, you're now equipped to make the most out of your NodeMCU board. Whether it's programming the LEDs for basic output or utilizing the GPIO pins for intricate projects, these insights form the foundation of your journey into the fascinating world of NodeMCU and IoT.</p> +<h2 id="programming-nodemcu">Programming NodeMCU<a class="zola-anchor" href="#programming-nodemcu" aria-label="Anchor link for: programming-nodemcu">#</a></h2> +<p>Programming is where your NodeMCU projects come to life, transforming lines of code into real-world actions and reactions. This section is dedicated to guiding you through the programming aspects of NodeMCU, from writing your first script to exploring advanced programming techniques. Whether you are a beginner or an experienced developer, understanding how to effectively program the NodeMCU is key to unlocking its full potential.</p> +<h3 id="writing-and-uploading-basic-scripts-e-g-blinking-led-example">Writing and Uploading Basic Scripts (e.g., Blinking LED Example)<a class="zola-anchor" href="#writing-and-uploading-basic-scripts-e-g-blinking-led-example" aria-label="Anchor link for: writing-and-uploading-basic-scripts-e-g-blinking-led-example">#</a></h3> +<p>A great starting point for programming the NodeMCU is the classic 'Blinking LED' example. This simple project is not just about turning an LED on and off; it's a fundamental step in understanding how to control the NodeMCU's GPIO pins through programming. To begin, you'll write a script in the Arduino IDE that toggles one of the onboard LEDs. This script will use basic commands to set the GPIO pin as an output, and then to turn the LED on (set the pin LOW) and off (set the pin HIGH) in a loop.</p> +<p>Uploading this script to your NodeMCU is straightforward. With your NodeMCU connected to your computer via a micro-USB cable and the correct board and port selected in the Arduino IDE, simply click the 'Upload' button. The IDE compiles the script into bytecode, which is then uploaded to the NodeMCU. Once uploaded, the NodeMCU executes the script, and you should see the LED start to blink. This process not only demonstrates the basics of NodeMCU programming but also familiarizes you with the upload procedure and troubleshooting any issues that may arise, such as connection errors or syntax mistakes in your code.</p> +<h3 id="lua-vs-arduino-ide-pros-and-cons">Lua vs. Arduino IDE: Pros and Cons<a class="zola-anchor" href="#lua-vs-arduino-ide-pros-and-cons" aria-label="Anchor link for: lua-vs-arduino-ide-pros-and-cons">#</a></h3> +<p>NodeMCU offers the flexibility to program in Lua directly or use the Arduino IDE. Each method has its advantages and considerations. Lua, being a lightweight scripting language, is ideal for simple, standalone NodeMCU projects. It allows for quick development and iteration, with scripts being easier to write and modify. However, Lua might have limitations when it comes to more complex applications, especially those requiring extensive libraries and support that are readily available in the Arduino environment.</p> +<p>On the other hand, programming the NodeMCU using the Arduino IDE opens up a familiar environment for those who have worked with Arduino boards. It provides access to a vast library of existing code and community support, making it easier to implement complex functionalities. While the Arduino IDE offers more robust capabilities, it can be slightly more cumbersome for quick, small-scale projects compared to the straightforwardness of Lua scripting. Ultimately, the choice between Lua and Arduino IDE programming depends on your project requirements and personal preferences.</p> +<h3 id="advanced-programming-tips-and-tricks">Advanced Programming Tips and Tricks<a class="zola-anchor" href="#advanced-programming-tips-and-tricks" aria-label="Anchor link for: advanced-programming-tips-and-tricks">#</a></h3> +<p>As you become more comfortable with basic NodeMCU programming, you can explore advanced techniques to enhance your projects. This includes mastering the use of interrupts for responsive designs, leveraging WiFi capabilities for IoT connectivity, and optimizing power consumption for battery-operated devices. Understanding how to efficiently manage memory and utilize deep sleep modes can significantly improve the performance and durability of your NodeMCU projects.</p> +<p>Additionally, integrating external sensors and modules expands the functionality of your NodeMCU board. Learning how to interface with different types of sensors (like temperature, humidity, motion sensors) and communication modules (like Bluetooth or Zigbee) opens up a world of possibilities for your IoT projects. Experimenting with these advanced concepts not only broadens the scope of your projects but also deepens your understanding and skills in the realm of embedded systems and IoT.</p> +<p>Through this comprehensive overview of programming NodeMCU, you are now equipped with the knowledge to start creating, experimenting, and innovating. Whether it's writing simple scripts or diving into complex IoT applications, the world of NodeMCU programming is rich with opportunities for exploration and creativity.</p> +<h2 id="real-world-applications-and-projects">Real-World Applications and Projects<a class="zola-anchor" href="#real-world-applications-and-projects" aria-label="Anchor link for: real-world-applications-and-projects">#</a></h2> +<p>Exploring the practical applications of NodeMCU is where the true excitement lies for IoT enthusiasts and hobbyists. This section aims to bridge the gap between theory and real-world application, showcasing how NodeMCU can be utilized in various projects. From simple home automation to complex IoT systems, NodeMCU’s versatility makes it an ideal choice for a wide range of applications.</p> +<h3 id="sample-iot-projects-using-nodemcu">Sample IoT Projects using NodeMCU<a class="zola-anchor" href="#sample-iot-projects-using-nodemcu" aria-label="Anchor link for: sample-iot-projects-using-nodemcu">#</a></h3> +<p>Starting with home automation, a popular application for NodeMCU, you can create projects that control lights, thermostats, or even security systems through the internet. For instance, using NodeMCU, you can develop a system that lets you control your home lighting via a smartphone app. This involves programming the NodeMCU to connect to your Wi-Fi network and receive commands from your phone, which then triggers the connected lights through relay modules. Such a project not only introduces you to the basics of IoT but also provides practical, everyday utility.</p> +<p>Another exciting application is creating a weather station. By connecting various sensors to NodeMCU, such as temperature, humidity, and pressure sensors, you can collect real-time environmental data. This data can be sent to an IoT platform like ThingSpeak or your own server, allowing you to monitor and analyze weather patterns over time. This project not only enhances your understanding of sensor integration but also offers insights into data handling and cloud connectivity in IoT systems.</p> +<h3 id="community-contributions-and-case-studies">Community Contributions and Case Studies<a class="zola-anchor" href="#community-contributions-and-case-studies" aria-label="Anchor link for: community-contributions-and-case-studies">#</a></h3> +<p>The NodeMCU community is vast and active, with members constantly sharing innovative projects and ideas. Engaging with this community can provide invaluable insights and inspiration for your own projects. For example, many community members have shared their experiences in building smart irrigation systems using NodeMCU, which automatically water plants based on soil moisture levels. These case studies often include detailed instructions, code snippets, and troubleshooting tips, making them a great resource for learning and experimentation.</p> +<p>Another area where community contributions shine is in the development of custom IoT applications, such as pet feeders, smart mirrors, or personalized notification systems. These projects often combine various technologies like web development, database management, and mobile app development with NodeMCU, showcasing its ability to integrate seamlessly into broader technology ecosystems. By exploring these case studies, you not only learn about NodeMCU but also about how it interacts with other technologies to create comprehensive solutions.</p> +<p>This exploration of real-world applications and community projects highlights the practicality and adaptability of NodeMCU in a variety of settings. Whether you are looking to enhance your home, understand the environment, or connect with a global community of innovators, NodeMCU offers a platform to turn your ideas into reality. Through these applications, you'll see just how your skills in programming and electronics can have a tangible impact in the world.</p> +<h2 id="nodemcu-beyond-the-basics">NodeMCU: Beyond the Basics<a class="zola-anchor" href="#nodemcu-beyond-the-basics" aria-label="Anchor link for: nodemcu-beyond-the-basics">#</a></h2> +<p>After mastering the fundamentals of NodeMCU, it's time to push the boundaries and explore its advanced capabilities. This section delves into the more sophisticated aspects of NodeMCU, highlighting how it can be integrated with other technologies and platforms. By expanding your NodeMCU knowledge, you can take your projects from simple applications to complex, interconnected systems that represent the cutting edge of IoT development.</p> +<h3 id="advanced-modules-and-add-ons">Advanced Modules and Add-ons<a class="zola-anchor" href="#advanced-modules-and-add-ons" aria-label="Anchor link for: advanced-modules-and-add-ons">#</a></h3> +<p>One of the ways to enhance the capabilities of your NodeMCU is by integrating it with various modules and add-ons. For instance, adding a GPS module can transform your NodeMCU project into a location-aware device, perfect for tracking applications or smart vehicles. When integrating a GPS module, you'll delve into serial communication and parsing GPS data, expanding your technical skills and understanding of geolocation technologies.</p> +<p>Another exciting add-on is the incorporation of wireless communication modules, such as Zigbee or Bluetooth Low Energy (BLE). These modules enable your NodeMCU to communicate with other devices wirelessly, opening up possibilities for creating mesh networks, smart home systems, or even IoT hubs. Learning to integrate these modules involves understanding wireless communication protocols and network topology, further broadening your IoT expertise.</p> +<h3 id="integrating-nodemcu-with-other-iot-platforms">Integrating NodeMCU with Other IoT Platforms<a class="zola-anchor" href="#integrating-nodemcu-with-other-iot-platforms" aria-label="Anchor link for: integrating-nodemcu-with-other-iot-platforms">#</a></h3> +<p>NodeMCU's true potential is unleashed when it's integrated with other IoT platforms. For example, connecting NodeMCU with platforms like AWS IoT or Google Cloud IoT enables you to store, process, and analyze the data collected by your NodeMCU device. This integration requires an understanding of cloud services, data security, and network management, providing an excellent opportunity to delve into the world of cloud computing and big data.</p> +<p>Another integration avenue is using NodeMCU with home automation platforms like Home Assistant or OpenHAB. This integration allows you to create sophisticated home automation systems that are customizable and can interact with a wide range of devices. It involves learning about APIs, MQTT protocol, and smart home standards, offering a comprehensive look into the world of smart home technology.</p> +<h3 id="future-trends-and-developments-in-nodemcu-technology">Future Trends and Developments in NodeMCU Technology<a class="zola-anchor" href="#future-trends-and-developments-in-nodemcu-technology" aria-label="Anchor link for: future-trends-and-developments-in-nodemcu-technology">#</a></h3> +<p>As IoT continues to evolve, so does NodeMCU. Staying abreast of the latest trends and developments in NodeMCU technology is crucial for any IoT enthusiast. This includes keeping an eye on advancements in firmware, new modules, and emerging IoT protocols that could enhance the capabilities of NodeMCU.</p> +<p>Additionally, the growing focus on AI and machine learning in IoT presents exciting possibilities for NodeMCU projects. Integrating AI algorithms with NodeMCU, for applications like predictive maintenance or environmental monitoring, represents the next frontier in IoT development. This requires a foundational understanding of machine learning concepts and the ability to implement these algorithms in a resource-constrained environment like NodeMCU.</p> +<p>In this section, we've explored the advanced aspects of NodeMCU, from integrating with other technologies to staying updated with future trends. These insights are intended to inspire you to take your NodeMCU projects to new heights, leveraging its full potential to create innovative and impactful IoT solutions.</p> +<hr /> +<p>As we conclude our comprehensive journey through the world of NodeMCU, let's reflect on the rich tapestry of opportunities this versatile tool offers to IoT enthusiasts and hobbyists. From the initial exploration of its technical makeup and firmware options in Section II, where we delved into the intricacies of Lua and C programming, to the practical setup guidance in Section III, ensuring a smooth start to your NodeMCU experience. We journeyed through the crucial hardware components in Section IV, highlighting the significance of onboard controllers and GPIO pins for your projects.</p> +<p>Our adventure then took us into the realms of programming in Section V, where we learned to breathe life into our NodeMCU boards through scripts and advanced programming techniques. Section VI expanded our horizons further, showcasing real-world applications and community-driven projects, illustrating how NodeMCU acts as a cornerstone in various innovative IoT systems. Finally, in Section VII, we ventured beyond the basics to explore advanced modules, integrations with IoT platforms, and emerging trends, positioning NodeMCU at the forefront of IoT development.</p> +<p>Now, armed with this knowledge, the path lies open for you to embark on your own NodeMCU projects. Whether it's automating your home, building a personal weather station, or integrating advanced IoT technologies, the potential is limitless. Remember, the NodeMCU is not just a tool; it's a canvas for your creativity and a bridge to realizing your IoT dreams.</p> +<p>So, take that first step, experiment with a simple blinking LED project, or dive into more complex integrations. Each project, no matter the size, is a step forward in your IoT journey. And as you progress, stay connected with the vibrant NodeMCU community, drawing inspiration and sharing your experiences. Your journey with NodeMCU is just beginning, and the future is bright with possibilities. Embrace it with enthusiasm, and let your imagination lead the way in this exciting world of IoT and embedded systems.</p> + + + + Why run local LLMs? + 2023-11-30T15:27:00+00:00 + 2023-11-30T15:27:00+00:00 + + https://ntn888.github.io/blog/llama-why-local/ + <p>Have you ever wished your AI assistant was more flexible? More customized to your specific needs? More under your direct control? As remarkable as large language models like GPT-4 and Bard have been, relying solely on external APIs can leave developers feeling constrained. There's a better way – self-hosting your own private AI. </p> +<p>I distinctly remember the first time I used GPT-4. The sheer power and fluidity of its responses blew my mind. But after the initial wonder faded, limitations began to emerge. Requests for complex code would hit computation limits. Queries using sensitive data made me uneasy. And being dependent on a third-party API limited what customizations I could make.</p> +<p><img src="/img/friendly-chatbot.resized.png" alt="Friendly chat bot in space" /></p> +<p>Like Dorothy realizing her magical Oz was being powered by a man behind a curtain, shifting to a self-hosted large language model lifted the veil for me. Finally I had an AI assistant answering to me alone, trained on my data, living on my infrastructure. The flexibility, control, privacy, cost savings, and custom integrations unlocked have been a revelation. </p> +<p>As AI rapidly becomes essential business infrastructure, more teams are arriving at the same revelation. Self-hosted solutions allow you to mold a private AI assistant to your exact needs, without unpredictable costs or outside entanglements. Read on as we explore the benefits, options, and considerations shifting from rented to owned AI. I may be biased, but I believe you too will prefer having your friendly AI wizard in-house.</p> +<h2 id="more-flexibility-and-control">More Flexibility and Control<a class="zola-anchor" href="#more-flexibility-and-control" aria-label="Anchor link for: more-flexibility-and-control">#</a></h2> +<p>When it comes to leveraging AI, flexibility and control are everything. Relying on rigid external APIs can leave your options limited. By self-hosting a large language model, you open new dimensions of customization to tailor it precisely to your goals.</p> +<h3 id="fine-tuning-unlocks-targeted-performance">Fine-Tuning Unlocks Targeted Performance<a class="zola-anchor" href="#fine-tuning-unlocks-targeted-performance" aria-label="Anchor link for: fine-tuning-unlocks-targeted-performance">#</a></h3> +<p>Pre-trained models like GPT-4 and Bard display remarkable breadth, but their general knowledge comes at the cost of precision on specialized tasks. Fine-tuning allows you to customize models by continuing to train them on your own data, steering their intelligence toward your specific needs.</p> +<p>Whether improving code suggestions, analyzing scientific papers, or answering domain-specific questions, additional training refines their relevancy and accuracy. On-premise models give you the power over this enhancement process instead of being limited by a third party. The more tailored to your use cases, the better it performs.</p> +<h3 id="optimizing-compute-for-variable-demand">Optimizing Compute for Variable Demand<a class="zola-anchor" href="#optimizing-compute-for-variable-demand" aria-label="Anchor link for: optimizing-compute-for-variable-demand">#</a></h3> +<p>Self-hosted AI allows you to scale resources fluidly to meet fluctuating needs. Server capacity can expand seamlessly via the cloud to handle spikes in model queries. When demand decreases, reduce resources accordingly. This saves substantially versus paying for peak API capacity at all times.</p> +<p>The ability to load balance queries across devices and data centers further bolsters flexible distribution of compute. You also retain control to upgrade to new hardware quickly instead of relying on a provider’s refresh cycle, keeping model performance on the cutting edge.</p> +<h3 id="agility-to-update-experiment-and-improve">Agility to Update, Experiment and Improve<a class="zola-anchor" href="#agility-to-update-experiment-and-improve" aria-label="Anchor link for: agility-to-update-experiment-and-improve">#</a></h3> +<p>On-premise models empower your team to rapidly iterate experiments and innovations in AI capabilities. Changes don’t require coordination across organizations or account managers. Want to try enhancing prompts or expanding the training dataset? On your own systems, new ideas can be tested straightaway.</p> +<p>Having your AI engine in-house frees you to build additional tools and custom interfaces tailored to your workflows. As new techniques and model architectures emerge, self-hosted AI means you dictate the integration pace rather than being locked into a rigid vendor roadmap. With great power over your AI comes great product responsibility – are you ready?</p> +<h2 id="enhanced-privacy-and-security">Enhanced Privacy and Security<a class="zola-anchor" href="#enhanced-privacy-and-security" aria-label="Anchor link for: enhanced-privacy-and-security">#</a></h2> +<p>When relying on external AI systems, privacy and security considerations can quickly spiral into headaches. By retaining direct control over data and models on internal infrastructure, self-hosted LLMs simplify safeguarding information while meeting compliance needs.</p> +<h3 id="sensitive-data-stays-onsite">Sensitive Data Stays Onsite<a class="zola-anchor" href="#sensitive-data-stays-onsite" aria-label="Anchor link for: sensitive-data-stays-onsite">#</a></h3> +<p>Every industry deals with confidential data, whether customer info, financials, medical history or IP. Transmitting this data externally to utilize AI APIs rightly raises alarms for security and compliance teams. Just because insights from the data have business value doesn't mean IT oversight should be bypassed.</p> +<p>Self-hosted AI eliminates these concerns by keeping all processing onsite. Data remains within your firewall at all times, visible only to approved internal teams. With proper access controls, even admins running the AI systems can be prevented from directly viewing sensitive information used to train models.</p> +<h3 id="streamlining-regulatory-and-policy-compliance">Streamlining Regulatory and Policy Compliance<a class="zola-anchor" href="#streamlining-regulatory-and-policy-compliance" aria-label="Anchor link for: streamlining-regulatory-and-policy-compliance">#</a></h3> +<p>From financial regulations like GDPR to healthcare rules like HIPAA, evolving legal expectations make safe data handling trickier by the year. By retaining data and AI systems in-house instead of relying on cloud services, the barriers to compliance are dramatically reduced.</p> +<p>Your own infrastructure allows auditing and controls tailored to your exact regulatory needs, with less dependence on third-party attestations. Data residency laws also come into play requiring information stay within national borders, easily addressed via on-premise solutions.</p> +<h3 id="cutting-external-dependencies-improves-security-posture">Cutting External Dependencies Improves Security Posture<a class="zola-anchor" href="#cutting-external-dependencies-improves-security-posture" aria-label="Anchor link for: cutting-external-dependencies-improves-security-posture">#</a></h3> +<p>Every external API call or cloud service dependency increases vulnerabilities by expanding the corporate attack surface. Just look at the barrage of stabilizer AI incidents last year! Self-hosted models help prevent such headaches by eliminating external connections associated with AI functions.</p> +<p>Isolation also enables creating something like an &quot;air gap&quot; via machinery only used for model handling, disconnected from wider business networks. Though not bulletproof, minimizing touch points via private AI infrastructure pushes security in the right direction.</p> +<h2 id="lower-long-term-costs">Lower Long-Term Costs<a class="zola-anchor" href="#lower-long-term-costs" aria-label="Anchor link for: lower-long-term-costs">#</a></h2> +<p>When evaluating the financial implications of AI systems, it's essential to take a big picture perspective. Though recognizing ongoing costs, over years self-hosted solutions often prove far more economical than reliance on rental APIs requiring endless subscription fees.</p> +<h3 id="avoiding-mounting-api-expenses">Avoiding Mounting API Expenses<a class="zola-anchor" href="#avoiding-mounting-api-expenses" aria-label="Anchor link for: avoiding-mounting-api-expenses">#</a></h3> +<p>It's easy to only compare upfront expenses when adopting new technology, failing to account for recurring fees endless draining budgets over time. Leading LLM APIs often run $0.002+ per 1,000 tokens processed. For context, this essay already tallied over 3,400 tokens – costing over $6 at that rate!</p> +<p>While the convenience of instantly available AI is appealing, even moderate enterprise usage adds up to staggering sums. Budget-conscious leaders rightly question chiefly benefiting API shareholder value long-term for functionality becoming a commodity.</p> +<h3 id="leveraging-existing-infrastructure">Leveraging Existing Infrastructure<a class="zola-anchor" href="#leveraging-existing-infrastructure" aria-label="Anchor link for: leveraging-existing-infrastructure">#</a></h3> +<p>Rather than building from scratch, self-hosted AI can integrate with current on-premise servers and hardware many enterprises already own. Though still representing an investment, extending existing capacity is far cheaper than standalone rental expenses.</p> +<p>Private AI also allows you to dictate upgrade cycles rather than relying on a provider's hardware refresh rate. Regular advances in GPU/TPU processing mean efficiency gains offsetting growing model sizes. In five years the compute powering innovations today could easily fit on a desktop.</p> +<h3 id="improved-return-as-models-compound-gains">Improved Return as Models Compound Gains<a class="zola-anchor" href="#improved-return-as-models-compound-gains" aria-label="Anchor link for: improved-return-as-models-compound-gains">#</a></h3> +<p>A core advantage of LLMs lies in their ability to build upon prior learning. Over months and years of consistent data exposure, even hosted locally their performances continue improving. This means models become an appreciating asset intrinsically delivering multiplying value beyond static rental APIs.</p> +<p>With customer interactions, new products, and technical advances expanding data pools,Compose even longer form content with deeper analysis the accuracy and quality of outputs compound faster on privately controlled infrastructure. Much like wine aging to perfection in your cellar.</p> +<h2 id="easier-integration-and-customization">Easier Integration and Customization<a class="zola-anchor" href="#easier-integration-and-customization" aria-label="Anchor link for: easier-integration-and-customization">#</a></h2> +<p>Beyond core model functionality, realizing AI's full potential requires tailored integration with business systems and processes. Self-hosted infrastructure fosters frictionless customization that rented APIs simply can't match.</p> +<h3 id="streamlining-connections-to-internal-data-apps">Streamlining Connections to Internal Data &amp; Apps<a class="zola-anchor" href="#streamlining-connections-to-internal-data-apps" aria-label="Anchor link for: streamlining-connections-to-internal-data-apps">#</a></h3> +<p>Extracting maximum value from AI necessitates easy interoperability with other stacks powering operations. Self-hosted models co-located on-premise simplify linking to internal databases, analytics tools, CRM and ERP platforms etc. without external touchpoints.</p> +<p>With direct data access, models can programmatically pull the latest info and update training without manual efforts. Code can connect to other apps via API allowing AI to enhance workflows across departments. Avoid integration hassles or changes breaking links to external providers.</p> +<h3 id="building-custom-interfaces-experiences">Building Custom Interfaces &amp; Experiences<a class="zola-anchor" href="#building-custom-interfaces-experiences" aria-label="Anchor link for: building-custom-interfaces-experiences">#</a></h3> +<p>The client interface heavily impacts perceived AI quality by employees and customers. Rented APIs mean being stuck with vanilla experiences, but self-hosted options empower developing bespoke tools aligned to your brand.</p> +<p>Beyond skins and themes, you can tailor interactions to specific audiences within your organization. Data scientists may prefer Python notebooks while business analysts appreciate no-code web UIs. White label the output for customer facing applications. The sky's limit when controlling both model and interface.</p> +<h3 id="innovating-new-products-services">Innovating New Products &amp; Services<a class="zola-anchor" href="#innovating-new-products-services" aria-label="Anchor link for: innovating-new-products-services">#</a></h3> +<p>Owning the full AI stack fosters launching entirely new solutions. As examples, AI could personalized customer marketing content, analyze warranty claims and highlight areas for engineering improvements, or bootstrap insurance policy document review.</p> +<p>With the flexibility to evolve models and build around their capabilities, you're only limited by imagination rather than restrictions imposed by external platforms. Build a strategic advantage by doubling down on proprietary efforts rivals can’t replicate relying on vendors.</p> +<h2 id="conclusion">Conclusion<a class="zola-anchor" href="#conclusion" aria-label="Anchor link for: conclusion">#</a></h2> +<p>As we've explored across critical areas like flexibility, security, costs, and customization, self-hosting your own large language model for private use provides transformational advantages compared to reliance on external AI rental services.</p> +<p>By retaining direct control over your AI assistant within your own infrastructure, you gain unmatched ability to customize to your specific data, workflows, and evolving needs. Keeping processing on-premise together with the sensitive information used for training also slashes compliance risks and data privacy concerns.</p> +<p>And while upfront investment is required, over the long-term self-hosted models can significantly reduce expenses versus open-ended API subscriptions. All while better leverage of existing systems and multiplying accuracy through continual learning compound benefits.</p> +<p>I encourage you to seriously pursue bringing customized AI capabilities in-house. Start small if needed (See <a href="https://ntn888.github.io/blog/llama-howto/">this post</a> for inspiration!), but the long-term dividends across security, costs, and performance make owning your AI absolutely worthwhile. Just be careful as it can become highly addictive once tuned to your goals!</p> +<p>The essential next step is evaluating options matching deployment and training requirements, but with the right vision the loops of constant improvement can truly make a private AI assistant feel like your devoted partner in innovation. Here's to a more customized, controlled AI future.</p> + + + + 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. 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. The following has been exctracted from <a rel="nofollow noreferrer" href="https://are-we-gfx1100-yet.github.io/post/a1111-webui/#prerequisites">here</a>.</p> +<pre class="z-code"><code><span class="z-text z-plain"># install the amdgpu driver with rocm support +curl -O https://repo.radeon.com/amdgpu-install/5.6/ubuntu/jammy/amdgpu-install_5.6.50600-1_all.deb +sudo dpkg -i amdgpu-install_5.6.50600-1_all.deb +</span></code></pre> +<p>Now edit the install script at <code>/usr/bin/amdgpu-install</code> via a text editor and add <code>|pop</code> next to <code>ubuntu</code> like so:</p> +<pre class="z-code"><code><span class="z-text z-plain">case &quot;$ID&quot; in +ubuntu|linuxmint|debian|pop) + ... +</span></code></pre> +<pre class="z-code"><code><span class="z-text z-plain"># opencl might cause issues later, so skip it unless you need it +sudo amdgpu-install --usecase=graphics,rocm --no-dkms + +# grant current user the access to gpu devices +sudo usermod -aG video $USER +sudo usermod -aG render $USER + +# reboot is needed to make both driver and user group take effect +sudo reboot +</span></code></pre> +<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>The <code>n-gpu-layers</code> is a parameter you get when loading the GGUF models; which can scale between the GPU and CPU as you see fit! So using this parameter you can select, for example, 32 out of the 35 (the max for our <code>zephyr-7b-beta</code> model) to be offloaded to the GPU by selecting 32 here.</p> +<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> +<p>But admittedly it is a fast moving landscape. There's new strides being made <em>every single day</em>. And many claim it wouldn't be the distant future that a 7b model easily outperfoms the current performance of GPT-4! And I can't wait for that day to self-host it on my humble 8Gig card.</p> +<blockquote> +<p>UPDATE: I installed Ubuntu Server 22.04 on the PC and converted it into a server (and access textgen web ui remotely). Ubuntu being fully supported by AMD; you can omit the <code>--no-dkms</code> flag above to install the kernel modules. With this I've seen a significant improvement in responsiveness. It starts spitting out the text almost instantly (similar to my experiences with vast.ai instances) and get a consistently higher tokens/s. Therefore I do not recommend a POP! OS system.</p> +</blockquote> + + + + 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<sup class="footnote-reference"><a href="#1">1</a></sup>.</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> +<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup> +<p>I've finally managed to run it. See <a href="https://ntn888.github.io/blog/llama-howto/">this post</a> for updated info.</p> +</div> + + + + 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>Are you ready to embark on an exciting adventure through the unknown territories of electronic measurement? If so, get your toolkit handy and prepare to experience an unforgettable journey as we dive deep into the mysterious world of multimeters! In this tantalizing guide, we'll explore a myriad of intriguing features, ground-breaking techniques, captivating personal experiences... and, of course, how they all fit together in making electronic measurements fun and hassle-free for both amateur enthusiasts and expert technicians alike. So buckle up, strap on your thinking cap, because it's time to discover the true potential of our multimeter pals!</p> +<p><img src="/img/multimeter_using.jpeg" alt="Using a multimeter" /></p> +<h2 id="key-features-and-specifications-of-multimeters">Key Features and Specifications of Multimeters<a class="zola-anchor" href="#key-features-and-specifications-of-multimeters" aria-label="Anchor link for: key-features-and-specifications-of-multimeters">#</a></h2> +<p>Now let's discuss those vital functions you will find on a multimeter: Voltage mode measures AC or DC voltage (in volts) while Resistance (Ohm) measurement provides the basic information about electrical components in a circuit... but there’s more! Did you know that these devices can also measure other interesting parameters like capacitance, frequency, and even determine if two conductive points touch each other, thanks to its inductive range and continuity checker function? Fascinating, right?! ✨ Each feature has its significance - trust us on this one! We encourage you not only to understand their meaning but also how they operate with different types of components and setups.</p> +<p>Understanding the various measurements and features that a multimeter offers goes beyond technical knowledge – it enhances problem-solving abilities as well! Plus, knowing all this adds depth to your hobby/ profession and showcases competency at the workplace (impressive bonus points there!) Remember, these little guys are far more capable than we realize initially. So let's unleash their full potential together!</p> +<h2 id="types-of-multimeters-from-analog-to-digital-modes">Types of Multimeters: From Analog to Digital Modes<a class="zola-anchor" href="#types-of-multimeters-from-analog-to-digital-modes" aria-label="Anchor link for: types-of-multimeters-from-analog-to-digital-modes">#</a></h2> +<p>Let’s start with our trusty companion - the Analog Multimeter (or as your grandparents called them, 'Old Skool Dudes'). These bad boys operate using needle and dial displays which some purists claim to provide a more analog sense (hmm... we thought they were just being picky). Despite that eccentricity, these veterans offer good readings at an affordable cost. The major drawback is they are relatively less precise due to user calibration error. However, they still remain popular for simple diagnostics and testing tasks.</p> +<p>In contrast stands our high-tech warrior - the Digital Multimeter (AKA 'Smarty Pants Meter'), featuring easy-to-read LCD screens offering exact results! Thanks to its advanced circuitry, these digital beasts provide unmatched accuracy and a plethora of additional features including data storage, auto shutdown, overload protection... need I say more? 😍 It’s almost like having a personal assistant! Remember though, although they have cool bells &amp; whistles, learning how to operate them takes time due to their intricate technology. But hey, who said getting smarter wasn't worth some extra effort? </p> +<h2 id="safety-measures-when-using-multimeters">Safety Measures When Using Multimeters<a class="zola-anchor" href="#safety-measures-when-using-multimeters" aria-label="Anchor link for: safety-measures-when-using-multimeters">#</a></h2> +<p>We're delving deeper into multimeter safety measures: everything from tips for dealing with live electricity to proper handling techniques and personal protective equipment (PPE) recommendations. Trust us, you don't want to skimp on these crucial aspects! Remember when mamma said 'safety first?!' Well, she was right all along.</p> +<p>When we say live electricity, we mean the type of power source you might find running through electrical wiring in your house or car - let's call it Mr. High-Voltagie. It packs a serious punch and if not handled correctly can lead to some nasty surprises like shocks, fires, or even death (scary, right?). Therefore, always unplug the device you want to measure before connecting your meter... just think of it as polite electricity etiquette. 😄</p> +<p>As for proper handling techniques, one key rule to remember is NEVER TOUCH THE PROBE TIPS! Always touch the metal parts (cause who likes getting zapped anyways?). Another important reminder - never leave the probes connected to the power source while not in use because it could short circuit your equipment. Sounds complex, right? But trust us, with practice and attention, you'll become an expert multimeter handler in no time! </p> +<h2 id="common-use-cases-for-multimeters-auto-repair-renewable-energy-more">Common Use Cases for Multimeters: Auto Repair, Renewable Energy, &amp; More!<a class="zola-anchor" href="#common-use-cases-for-multimeters-auto-repair-renewable-energy-more" aria-label="Anchor link for: common-use-cases-for-multimeters-auto-repair-renewable-energy-more">#</a></h2> +<p>How about trying self-diagnosis during auto maintenance? I bet that'll impress your mechanic (just don't tell them, though). Multimeters come in handy for troubleshooting engine issues, electrical problems, and so much more. It’s like having a mini toolbox that tells you exactly what's going wrong under the hood!</p> +<p>Another interesting use case? Renewable energy systems (e.g., solar panels) depend heavily on multimeters for maintenance and optimization. Ever thought about working towards saving Mother Earth while mastering new skills at the same time? There you go - your chance!</p> +<p>Let's not forget those living off-grid or experimenters playing with alternative energy sources; trusty old multimeter can be a game-changer here, too. Monitoring power consumption, voltage fluctuation... it becomes so much easier and safer than trying to read your appliances by the light they emit (although I admit, that was a funny experiment!).</p> +<p>Remember folks - every use case brings unique challenges and opportunities; embrace them all, expand your skillset, grow as an electronic problem solver. The sky's the limit when you're armed with such a versatile tool like a multimeter!</p> +<h2 id="tips-for-choosing-your-perfect-multimeter-features-cost-accuracy-matters">Tips for Choosing Your Perfect Multimeter - Features, Cost, Accuracy Matters!<a class="zola-anchor" href="#tips-for-choosing-your-perfect-multimeter-features-cost-accuracy-matters" aria-label="Anchor link for: tips-for-choosing-your-perfect-multimeter-features-cost-accuracy-matters">#</a></h2> +<p>Firstly, let’s talk about safety measures. Safety glasses on, mateys! If a device can detect voltages from zero to more than sixty volts while also telling you the amount of amps passing through - it should be at the top of your list, trust us. Why? Well, because playing with dangerous current levels without adequate protection is not cool... it's downright reckless. ☠️⏱</p> +<p>Secondly, let’s consider cost factors. Some folks might opt for cheaper alternatives, believing they won't use their multimeters much after initial setup. But guess what? Quality doesn’t come cheap! Spend a bit more on a sturdy tool that offers better reliability over time; it saves money in the long run and peace of mind (totally worth it, right?).</p> +<p>As for accuracy... if your readings don't add up properly, well then things might get messy - think explosive experiments gone awry. Accuracy determines how precise those readings will be! Remember: no margin for error here, chummers! Look out for instruments capable of +/- 0.5% tolerance rating; they provide you with high precision measurements needed during troubleshooting tasks or lab work - a real lifesaver when accuracy is crucial! 🔍💖</p> +<p>And finally, my hearty mates - never underestimate personal preferences! Everyone has their own preferred 'kitchen gear'. You should feel at home using your multimeter; so look for one with user-friendly interface and design tailored to your hand preference or specific needs... after all, happy tools = happy you!</p> +<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> +<h2 id="final-thoughts-unlock-the-potential-of-electronic-measurements">Final Thoughts - Unlock the Potential of Electronic Measurements!<a class="zola-anchor" href="#final-thoughts-unlock-the-potential-of-electronic-measurements" aria-label="Anchor link for: final-thoughts-unlock-the-potential-of-electronic-measurements">#</a></h2> +<p>Well now, we've reached the end of our marvelous multimeter adventure - how about one last hooray? 🎉✨ This wonderful world of electronic measurement surely holds more secrets waiting to be unraveled by you! With each key feature explored in detail and tips given on choosing the perfect meter for your specific needs, embarking on these explorations with a trusty multimeter is no longer a daunting task. Remember folks, knowing where your voltage or current values stand can save time during troubleshooting or help you understand more about different circuits - truly powerful stuff!</p> +<p>So what’s next? It's simple: put your newfound knowledge into practice. You could start by conducting exciting DIY projects or even just trying to maintain and monitor everyday gadgets at home or work... imagine showing off to friends how precise their phone charger is performing, wouldn’t that be an ego boost?!</p> +<p>Here’s a little nugget of advice: invest in quality multi-meters since they're more durable and offer better reliability over time. After all, good tools inspire creative problem-solving! And who knows? Maybe your name will someday be etched amongst tech legends who changed the world through their brilliant inventions.</p> +<p>Alrighty then adventurers, gear up for even more exciting embedded electronics encounters here on this blog. It's been a blast exploring with you so far and we can't wait to continue this adventure together! Onward to our next exciting chapter - stay safe, experiment, learn!</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! <del>But I recommend buying as a combo for a piece of mind due to any compatiblity issues.</del> See UPDATE below.</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> +<hr /> +<p>UPDATE (31/12/23): On second thought, actually getting the parts separately instead of the MB/CPU/RAM Combo is a better idea. Thanks to the information available on <a rel="nofollow noreferrer" href="https://xeon-e5450.ru/">https://xeon-e5450.ru/</a>.</p> +<p>First search Ali for <code>x99</code> and pick for a good (popular) motherboard. Then head to xeon-e5450.ru and see for compatible RAM. They provide links for matching RAM. If available, going with server RAM is safer and cheaper! And finally check for compatible CPU. I believe the 2011-3 chip Xeon E5 2670v3 is always matched.</p> +<p>Using the above information I have since upgraded my RAM to 32GB ECC Server version and now use the machine as a Linux kernel compilation cruncher! It compliles the full kernel for an embedded system typically under 10mins.</p> +<p>Highly Recommended!</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:27:00+00:00 + 2023-11-03T00:27: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:27:00+00:00 + 2023-09-16T00:27: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:27:00+00:00 + 2023-05-23T00:27: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:27:00+00:00 + 2023-05-21T00:27: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:27:00+00:00 + 2023-05-17T00:27: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:27:00+00:00 + 2023-05-16T00:27: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:27:00+00:00 + 2023-05-11T00:27: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:27:00+00:00 + 2023-05-10T00:27: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 and compact, I prefer go with a second hand ex-lease SFF(small form-factor) machine. While this is usually cheaper; you also get a sturdy casing to house your hard drives. They can take a max of 2 storage HDDs. If you want to hold more you can explore other options such as tower PCs. Take a look at <a rel="nofollow noreferrer" href="https://forums.serverbuilds.net/t/guide-nas-killer-6-0-ddr4-is-finally-cheap/13956">NAS Killer builds</a> over at serverbuilds.net.</p> +<p>The used SFF machines come in great discounts on ebay seasonally (as low as $50), get one with 8G of memory and atleast a 4th-gen core-i5 processor. You wouldn't need a powerful machine for you NAS. But if you're streaming your media outside of your local network, plex/emby will have to do transcoding which will take up CPU resource.</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. <a href="https://ntn888.github.io/blog/radarr-profiles/">Here's</a> a post on setting up Radarr quality profiles, so that you pull those juicy, efficient, balanced x265 encodes.</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:27:00+00:00 + 2023-05-06T00:27: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:27:00+00:00 + 2023-05-05T00:27: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:27:00+00:00 + 2023-04-22T00:27: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:27:00+00:00 + 2022-12-27T00:27: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:27:00+00:00 + 2022-12-07T00:27: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:27:00+00:00 + 2022-12-01T00:27: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:27:00+00:00 + 2022-11-24T00:27: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:27:00+00:00 + 2022-11-23T00:27: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:27:00+00:00 + 2022-11-16T00:27: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:27:00+00:00 + 2022-11-16T00:27: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:27:00+00:00 + 2022-11-13T00:27: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:27:00+00:00 + 2022-11-04T00:27: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:27:00+00:00 + 2022-03-20T00:27: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:27:00+00:00 + 2022-03-19T00:27: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:27:00+00:00 + 2022-03-18T00:27: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:28:00+00:00 + 2022-03-17T00:28: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:27:00+00:00 + 2022-03-17T00:27: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:27:00+00:00 + 2022-03-16T00:27: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:27:00+00:00 + 2022-03-15T00:27: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:27:00+00:00 + 2022-02-27T00:27: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:27:00+00:00 + 2022-02-27T00:27: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:31:00+00:00 + 2022-02-24T00:31: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> + + + + PWM + 2022-02-24T00:30:00+00:00 + 2022-02-24T00:30: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> + + + + Timers + 2022-02-24T00:29:00+00:00 + 2022-02-24T00:29: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> + + + + Serial UART + 2022-02-24T00:28:00+00:00 + 2022-02-24T00:28: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> + + + + freeRTOS Overview + 2022-02-24T00:27:00+00:00 + 2022-02-24T00:27: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> + + + + Basic GPIO BL702 + 2022-02-23T00:27:00+00:00 + 2022-02-23T00:27: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:28:00+00:00 + 2022-02-22T00:28: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:27:00+00:00 + 2022-02-22T00:27: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:27:00+00:00 + 2022-02-20T00:27: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:27:00+00:00 + 2022-02-18T00:27: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> + + + + Welcome + 2022-02-16T00:27:00+00:00 + 2022-02-16T00:27: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> + + + + A DIY jtag debugger + 2022-02-16T00:26:00+00:00 + 2022-02-16T00:26: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> + + + \ 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/720p_1080p-quality-profile.png b/img/720p_1080p-quality-profile.png new file mode 100644 index 0000000..5c02427 Binary files /dev/null and b/img/720p_1080p-quality-profile.png differ diff --git a/img/ESP8266-NodeMCU-kit-12-E-pinout-gpio-pin.webp b/img/ESP8266-NodeMCU-kit-12-E-pinout-gpio-pin.webp new file mode 100644 index 0000000..e2e5643 Binary files /dev/null and b/img/ESP8266-NodeMCU-kit-12-E-pinout-gpio-pin.webp differ diff --git a/img/Lama-like.png b/img/Lama-like.png new file mode 100644 index 0000000..ee5d26f Binary files /dev/null and b/img/Lama-like.png 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/RalphyXD-like.png b/img/RalphyXD-like.png new file mode 100644 index 0000000..f471f65 Binary files /dev/null and b/img/RalphyXD-like.png 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/friendly-chatbot.resized.png b/img/friendly-chatbot.resized.png new file mode 100644 index 0000000..6cd1499 Binary files /dev/null and b/img/friendly-chatbot.resized.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/opi-zero3.png b/img/opi-zero3.png new file mode 100644 index 0000000..3aba9a0 Binary files /dev/null and b/img/opi-zero3.png differ diff --git a/img/quality-slider.png b/img/quality-slider.png new file mode 100644 index 0000000..47f354b Binary files /dev/null and b/img/quality-slider.png 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..5d97d51 --- /dev/null +++ b/index.html @@ -0,0 +1 @@ +Ajit Ananthadevan
avatar
Ajit Ananthadevan@ajit
Interested in everything Embedded.

🔧 Are you tired of grappling with pesky issues in your #embeddedelectronics project? ✋ Don't fret, our team has got your back! ✨ We are a group of experts passionate about helping fellow technicians and businesses conquer their toughest obstacles. 💪

💰 Our mission is simple: we provide cost-effective embedded electronics consultancy services with unbeatable customer support.📚 Need help implementing microcontrollers or selecting the right chips? 👀 Got you covered! Trying to get a firmware optimized, no problem at all! 🙌🏼

Our goal is to be an indispensable asset that streamlines your embedded electronics development process.🚀 Let's create remarkable innovations together!👩‍💻📲 Drop us a line now and watch your project soar high like an eagle in the tech skies! 🚀 ✨

Shoot us a mail! Or connect to me on Fiverr.


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..ee04fed --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,547 @@ + + + + https://ntn888.github.io/ + + + https://ntn888.github.io/blog/ + + + https://ntn888.github.io/blog/802-15-4-wireless/ + 2023-04-22T00:27:00 + + + https://ntn888.github.io/blog/aliexpress-intro/ + 2023-11-10T10:27:00 + + + https://ntn888.github.io/blog/analog-filters-primer/ + 2022-02-27T00:27:00 + + + https://ntn888.github.io/blog/basic-app-structure/ + 2022-02-22T00:28:00 + + + https://ntn888.github.io/blog/basic-gpio/ + 2022-02-23T00:27:00 + + + https://ntn888.github.io/blog/begin-embedded/ + 2023-11-05T18:27:00 + + + https://ntn888.github.io/blog/bsp-config/ + 2022-03-15T00:27:00 + + + https://ntn888.github.io/blog/building-lab/ + 2023-11-09T20:27:00 + + + https://ntn888.github.io/blog/buildroot-devicetree/ + 2023-12-21T15:27:00 + + + https://ntn888.github.io/blog/buildroot-setup/ + 2023-12-19T00:27:00 + + + https://ntn888.github.io/blog/c-linkerscripts/ + 2022-11-16T00:27:00 + + + https://ntn888.github.io/blog/debian-testing/ + 2023-05-11T00:27:00 + + + https://ntn888.github.io/blog/debug-bl702/ + 2022-03-17T00:28:00 + + + https://ntn888.github.io/blog/dev-env/ + 2022-02-22T00:27:00 + + + https://ntn888.github.io/blog/dev-station/ + 2023-05-23T00:27:00 + + + https://ntn888.github.io/blog/device-drivers-101/ + 2022-02-24T00:31:00 + + + https://ntn888.github.io/blog/distro-talk/ + 2024-01-09T12:27:00 + + + https://ntn888.github.io/blog/diy-jtag-debugger/ + 2022-02-16T00:26:00 + + + https://ntn888.github.io/blog/diy-nas/ + 2023-05-10T00:27:00 + + + https://ntn888.github.io/blog/doom/ + 2022-11-16T00:27:00 + + + https://ntn888.github.io/blog/drop-ship/ + 2023-05-17T00:27:00 + + + https://ntn888.github.io/blog/esp32c3/ + 2022-03-18T00:27:00 + + + https://ntn888.github.io/blog/freertos/ + 2022-02-24T00:27:00 + + + https://ntn888.github.io/blog/green-energy/ + 2023-05-21T00:27:00 + + + https://ntn888.github.io/blog/ipv6-box/ + 2023-09-16T00:27:00 + + + https://ntn888.github.io/blog/linux-embedded-howto/ + 2023-12-31T03:27:00 + + + https://ntn888.github.io/blog/linux-gaming/ + 2023-11-09T17:27:00 + + + https://ntn888.github.io/blog/linux-lkmpg-milkv/ + 2024-01-06T13:27:00 + + + https://ntn888.github.io/blog/linux-milkv-duo/ + 2023-12-27T15:27:00 + + + https://ntn888.github.io/blog/linux-prepare-fedora/ + 2024-01-10T07:27:00 + + + https://ntn888.github.io/blog/llama-howto/ + 2023-11-14T00:27:00 + + + https://ntn888.github.io/blog/llama-why-local/ + 2023-11-30T15: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-13T00:27:00 + + + https://ntn888.github.io/blog/mcu-selection-guide/ + 2022-03-16T00:27:00 + + + https://ntn888.github.io/blog/micro-bit-breakout/ + 2023-05-05T00:27:00 + + + https://ntn888.github.io/blog/multimeters/ + 2023-11-09T14:27:00 + + + https://ntn888.github.io/blog/net-commands/ + 2023-05-16T00:27:00 + + + https://ntn888.github.io/blog/nodemcu/ + 2023-12-01T00:27:00 + + + https://ntn888.github.io/blog/nrf-clones/ + 2023-11-07T20:27:00 + + + https://ntn888.github.io/blog/nrfmicro/ + 2022-03-20T00:27:00 + + + https://ntn888.github.io/blog/nuttx-supported-boards/ + 2022-02-18T00:27:00 + + + https://ntn888.github.io/blog/octave-cli/ + 2022-02-27T00:27:00 + + + https://ntn888.github.io/blog/pico-4m/ + 2022-11-04T00:27:00 + + + https://ntn888.github.io/blog/piracy/ + 2022-11-24T00:27:00 + + + https://ntn888.github.io/blog/postfix-mail/ + 2022-12-27T00:27:00 + + + https://ntn888.github.io/blog/project-gravity-detector/ + 2023-05-06T00:27:00 + + + https://ntn888.github.io/blog/pwm/ + 2022-02-24T00:30:00 + + + https://ntn888.github.io/blog/radarr-profiles/ + 2024-04-27T17:27:00 + + + https://ntn888.github.io/blog/riot-oled/ + 2023-11-10T01:27:00 + + + https://ntn888.github.io/blog/selfhosted-gitlab/ + 2022-11-23T00:27:00 + + + https://ntn888.github.io/blog/serial/ + 2022-02-24T00:28:00 + + + https://ntn888.github.io/blog/sipeed-bl702/ + 2022-12-01T00:27:00 + + + https://ntn888.github.io/blog/sphinx/ + 2022-12-07T00:27:00 + + + https://ntn888.github.io/blog/ssd1306/ + 2022-03-17T00:27:00 + + + https://ntn888.github.io/blog/ssg-comparison/ + 2023-11-05T15:27:00 + + + https://ntn888.github.io/blog/thoughts-esp-idf/ + 2022-03-19T00:27:00 + + + https://ntn888.github.io/blog/timers/ + 2022-02-24T00:29:00 + + + https://ntn888.github.io/blog/welcome/ + 2022-02-16T00:27:00 + + + https://ntn888.github.io/blog/x99-motherboards/ + 2023-11-08T11:27:00 + + + https://ntn888.github.io/blog/xt-zb1-bl702/ + 2022-02-20T00:27:00 + + + https://ntn888.github.io/blog/zola-review/ + 2023-11-05T06:27:00 + + + https://ntn888.github.io/blog/zola-switch/ + 2023-11-03T00:27:00 + + + https://ntn888.github.io/categories/ + + + https://ntn888.github.io/categories/asm/ + + + https://ntn888.github.io/categories/bl702/ + + + https://ntn888.github.io/categories/embedded-linux/ + + + 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/arduino/ + + + 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/buildroot/ + + + 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/embedded-linux/ + + + 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-distro/ + + + https://ntn888.github.io/tags/linux-drivers/ + + + https://ntn888.github.io/tags/linux-gamimg/ + + + https://ntn888.github.io/tags/linux/ + + + 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/milkv-duo/ + + + 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/wifi/ + + + 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..13bf622 --- /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..6358878 --- /dev/null +++ b/tags/ai/index.html @@ -0,0 +1,3 @@ +My Blog
Why run local LLMs? 2023-11-30 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..cec38f5 --- /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/arduino/index.html b/tags/arduino/index.html new file mode 100644 index 0000000..8fc1829 --- /dev/null +++ b/tags/arduino/index.html @@ -0,0 +1,3 @@ +My Blog
# ArduinoAll Tags
NodeMCU: The chip powering countless DIY IoT projects, NodeMCU brings the cloud right onto your board 2023-12-01
\ No newline at end of file diff --git a/tags/assembly/index.html b/tags/assembly/index.html new file mode 100644 index 0000000..021ff7e --- /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..af36339 --- /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..81aed15 --- /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 PWM 2022-02-24 Timers 2022-02-24 Serial UART 2022-02-24 freeRTOS Overview 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..b0caf7d --- /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..b932eac --- /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/buildroot/index.html b/tags/buildroot/index.html new file mode 100644 index 0000000..982d15a --- /dev/null +++ b/tags/buildroot/index.html @@ -0,0 +1,3 @@ +My Blog
# buildrootAll Tags
Prepare Fedora Linux for Buildroot compilation 2024-01-10 Learning Embedded Linux Driver Development 2023-12-31 Milk-V Duo: $5 Embedded Linux board 2023-12-27 Booting a custom device tree file source in buildroot 2023-12-21 Set-up a cross-compile environment for buildroot Linux system running on Orange PI Zero 3 2023-12-19
\ No newline at end of file diff --git a/tags/debug/index.html b/tags/debug/index.html new file mode 100644 index 0000000..802821b --- /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..d41773e --- /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..42f04b6 --- /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..117af84 --- /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..48efce5 --- /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..690253d --- /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..ed7b39c --- /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..3efb59b --- /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..66f6877 --- /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..2b15228 --- /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..52306b2 --- /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..83dd4b1 --- /dev/null +++ b/tags/embedded-hobbiest/index.html @@ -0,0 +1,3 @@ +My Blog
# embedded hobbiestAll Tags
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..7f61d76 --- /dev/null +++ b/tags/embedded-hobbyist/index.html @@ -0,0 +1,3 @@ +My Blog
# embedded hobbyistAll Tags
NodeMCU: The chip powering countless DIY IoT projects, NodeMCU brings the cloud right onto your board 2023-12-01 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 Review: PI Pico 4MB version 2022-11-04 Switching to the nRFMicro 2022-03-20 Low-cost microcontrollers for hobbyists 2022-03-16
\ No newline at end of file diff --git a/tags/embedded-linux/index.html b/tags/embedded-linux/index.html new file mode 100644 index 0000000..eaba3c3 --- /dev/null +++ b/tags/embedded-linux/index.html @@ -0,0 +1,3 @@ +My Blog
# embedded-linuxAll Tags
Prepare Fedora Linux for Buildroot compilation 2024-01-10 Following the Linux Kernel Module Programming Guide with RISC-V Milkv Duo embedded board 2024-01-06 Learning Embedded Linux Driver Development 2023-12-31 Milk-V Duo: $5 Embedded Linux board 2023-12-27 Booting a custom device tree file source in buildroot 2023-12-21 Set-up a cross-compile environment for buildroot Linux system running on Orange PI Zero 3 2023-12-19
\ 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..950f68d --- /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..414bf0b --- /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..c20defa --- /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..d8dca30 --- /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..6ecc79c --- /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..ea613d4 --- /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..b3c68b6 --- /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..5208492 --- /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..654016d --- /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..f999e11 --- /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..3ce8bf3 --- /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..00a771b --- /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..1599866 --- /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-distro/index.html b/tags/linux-distro/index.html new file mode 100644 index 0000000..87434d8 --- /dev/null +++ b/tags/linux-distro/index.html @@ -0,0 +1,3 @@ +My Blog
# linux distroAll Tags
My roundup of some popular Linux Distros 2024-01-09
\ No newline at end of file diff --git a/tags/linux-drivers/index.html b/tags/linux-drivers/index.html new file mode 100644 index 0000000..b10a477 --- /dev/null +++ b/tags/linux-drivers/index.html @@ -0,0 +1,3 @@ +My Blog
# linux driversAll Tags
Following the Linux Kernel Module Programming Guide with RISC-V Milkv Duo embedded board 2024-01-06 Learning Embedded Linux Driver Development 2023-12-31
\ 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..30772f2 --- /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/linux/index.html b/tags/linux/index.html new file mode 100644 index 0000000..23ffa05 --- /dev/null +++ b/tags/linux/index.html @@ -0,0 +1,3 @@ +My Blog
# linuxAll Tags
Prepare Fedora Linux for Buildroot compilation 2024-01-10 My roundup of some popular Linux Distros 2024-01-09 Following the Linux Kernel Module Programming Guide with RISC-V Milkv Duo embedded board 2024-01-06 Learning Embedded Linux Driver Development 2023-12-31 Milk-V Duo: $5 Embedded Linux board 2023-12-27 Booting a custom device tree file source in buildroot 2023-12-21 Set-up a cross-compile environment for buildroot Linux system running on Orange PI Zero 3 2023-12-19
\ No newline at end of file diff --git a/tags/llamav2/index.html b/tags/llamav2/index.html new file mode 100644 index 0000000..55aa497 --- /dev/null +++ b/tags/llamav2/index.html @@ -0,0 +1,3 @@ +My Blog
# llamav2All Tags
Why run local LLMs? 2023-11-30 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..d82143f --- /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..8c2f849 --- /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..cbff19f --- /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..89aa2ca --- /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..2701166 --- /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/milkv-duo/index.html b/tags/milkv-duo/index.html new file mode 100644 index 0000000..c0ccfc2 --- /dev/null +++ b/tags/milkv-duo/index.html @@ -0,0 +1,3 @@ +My Blog
# milkv-duoAll Tags
Prepare Fedora Linux for Buildroot compilation 2024-01-10 Following the Linux Kernel Module Programming Guide with RISC-V Milkv Duo embedded board 2024-01-06
\ No newline at end of file diff --git a/tags/nas/index.html b/tags/nas/index.html new file mode 100644 index 0000000..8f7c6bd --- /dev/null +++ b/tags/nas/index.html @@ -0,0 +1,3 @@ +My Blog
Setup Radarr for x265 encodes 2024-04-27 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..2818d1f --- /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..1915269 --- /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..719fdc3 --- /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..c02ff77 --- /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..a5561ce --- /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..b188bc8 --- /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..a8fc3bf --- /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..a7e5ed9 --- /dev/null +++ b/tags/opensource/index.html @@ -0,0 +1,3 @@ +My Blog
# opensourceAll Tags
Why run local LLMs? 2023-11-30 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..7df09f3 --- /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..ac19f6f --- /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..c577559 --- /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..a333664 --- /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..cac3462 --- /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..2431aa2 --- /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 PWM 2022-02-24 Timers 2022-02-24 Serial UART 2022-02-24 freeRTOS Overview 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..c3407af --- /dev/null +++ b/tags/seedbox/index.html @@ -0,0 +1,3 @@ +My Blog
# seedboxAll Tags
Setup Radarr for x265 encodes 2024-04-27 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..9babfe8 --- /dev/null +++ b/tags/self-host/index.html @@ -0,0 +1,3 @@ +My Blog
# self-hostAll Tags
Setup Radarr for x265 encodes 2024-04-27 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..1a3a476 --- /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..ca63ea3 --- /dev/null +++ b/tags/selfhosted/index.html @@ -0,0 +1,3 @@ +My Blog
# selfhostedAll Tags
Why run local LLMs? 2023-11-30 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..0890950 --- /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..39bcb13 --- /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..5d06143 --- /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..110accc --- /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..12fe5c3 --- /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..f0f1b56 --- /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..b68c2bd --- /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..7ebd12e --- /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..0050b54 --- /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..6ae3be6 --- /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..5fd5a91 --- /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..8b80554 --- /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..3b1e013 --- /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..2fb554e --- /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..0c6dc7f --- /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/wifi/index.html b/tags/wifi/index.html new file mode 100644 index 0000000..bf56384 --- /dev/null +++ b/tags/wifi/index.html @@ -0,0 +1,3 @@ +My Blog
# wifiAll Tags
NodeMCU: The chip powering countless DIY IoT projects, NodeMCU brings the cloud right onto your board 2023-12-01
\ No newline at end of file diff --git a/tags/workstation/index.html b/tags/workstation/index.html new file mode 100644 index 0000000..5a19800 --- /dev/null +++ b/tags/workstation/index.html @@ -0,0 +1,3 @@ +My Blog
# workstationAll Tags
My roundup of some popular Linux Distros 2024-01-09 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..1d4687e --- /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