Coffee and Contemplation A developer blog

Moving to NixOs

The idea of a declartive reproducible operating system is really enticing. NixOs has been popping up in my reddit feeds recently. I had an option to install Nix as a package manager on my existing linux installation. But I decided to jump in the deep end. So, this post is being written on NixOS.

There is abundant documentation on Nix. Even after ignoring all of that, I was able to setup a working installation with most of my setup from the old Arch installation. Xmonad works. Firefox, telegram, zsh and neovim works too.

Setting up development environments is a bit tricky. It looks like there are different methods to set up isolated dev environments - nix shell, nix flakes, devenv etc. I got a flake based setup for python working. For android studio based android development, I had to install it globally. Flutter - there is no luck so far.

The next step is to learn the nix language and how the package management works.

Temperature logger improvements

Part1
Part2

Using the on-board LED to monitor sensor reads

The raspberry pi pico based temperature logger “hung” at times. It didn’t write anything to the file at all. But, it wasn’t obvious that it had stopped working. Initially, I had plans to turn on the on-board LED when writing to a file. But, more experienced programmers suggested that sensor_temp.read_u16() is more likely to be the blocking call. So turning on the LED before reading the sensor and turning it off after would give a clear indication. The LED will remain on if the call is blocked.

led = Pin(25, Pin.OUT)

def get_temperature():
    led.on()
    reading = sensor_temp.read_u16() * conversion_factor 
    temperature = 27 - (reading - 0.706)/0.001721
    led.off()
    return temperature

Logging time along with temperature

It looks like the pico has a real time clock and can be accessed with machine.RTC(). When running the code using Thonny, it initializes the RTC with the current time. But when running outside of thonny, the RTC should get set to midnight of Jan 1, 2015. We’ll write the time along with the temperature to get a sense of when(/if) the pico fails to write to the file.

rtc=machine.RTC()
now = rtc.datetime()
file.write("{hh:02d}:{mm:02d}:{ss:02d}, {temperature:.2f}\n"
    .format(hh=now[4], mm=now[5], ss=now[6], temperature=temperature))

Plotting a timeseries graph with gnuplot

So, there was this raspberry pi contraption to log temperature to a file. The next step is to plot this in a graph. We get a single column file of recorded temperatures from the raspberry pi pico.

It is pretty easy to plot a graph from a two column file using gnuplot. If the filename is temp_log, all you need is:

plot 'temp_log' with lines

I was too lazy to find how to plot a single column file. The easy fix is to add another column with numbers. And that can be done easily with vim. You’d need to do the following keystrokes:

ctrl-v shift-g I 0 <space> <esc> - This will add a column of zeroes to the beginning

gv - This will reselect the column of zeroes

g ctrl-a - This will change the zeroes to an increasing sequence of numbers

asciicast

Running gnuplot -persist plot.plt gets you the graph

Shower thought

  • Boards like these don’t have a battery and can’t keep real clock time. I have never thought of that before. It would have been easy if we could log the time along with the temperature.

Next steps

  • Blink an LED after writing to the file.
  • Use to_us_since_boot from the SDK while logging temperature.

Temperature monitoring with raspberry pi

We bought a new Air conditioner for the house. It doesn’t feel like the AC is able to regulate the temperature to what we set. But, humans are often wrong about these kind of things. A simple thermometer would have done the trick - but we need time-series temperature data to verify the AC function.

Enter - the Raspberry pi pico. It has a built-in ambient temperature sensor and supports micropython. It also has 2MB of storage. So, logging the temperature to a file should be easy.

This is the “finished” project:

The OLED screen displays the latest temperature reading. Interfacing with the display was very easy. Tom’s hardware has a very good guide explaining this. Reading the temperature was trivial too.

The dirty code I put together looked like this

import machine
import utime
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C

i2c=I2C(0,sda=Pin(0), scl=Pin(1), freq=400000)
oled = SSD1306_I2C(128, 64, i2c)
 
sensor_temp = machine.ADC(4)
conversion_factor = 3.3 / (65535)

file = open("temp_log.csv","a")

def get_temperature():
    reading = sensor_temp.read_u16() * conversion_factor 
    temperature = 27 - (reading - 0.706)/0.001721
    return temperature

while True:
    temperature = get_temperature()
    print(temperature)
    oled.fill(0)
    oled.text("Temp: %.2f" % temperature, 10, 10)
    oled.show()
    file.write("%.2f\n" % temperature)
    file.flush()
    utime.sleep(20)

The code worked most of the time. But, there were instances when the temperature reading just wouldn’t change. Also, nothing was written to the file. Some debugging is required to find and fix the issue.

Random learnings

  • If you name the file as main.py, it will run when the pico is powered on.
  • "%0.2f" will convert a float to a string and round off to 2 decimal places.
  • A power bank is good enough to power the pico away from other power sources.

Autocomplete in neovim with built-in LSP client

In the last blog post, we saw how to setup the built-in lsp client in neovim for diagnostics and such. Now we’ll see how to setup autocomplete.

First, install completion-nvim. Add this to your vimrc and run PlugInstall.

Plug 'nvim-lua/completion-nvim'

Now, in the lsp_config.lua file, you need to make some changes. The file should look like this:

lspconfig = require'lspconfig'
completion_callback = require'completion'.on_attach

lspconfig.pyls.setup{on_attach=completion_callback}
lspconfig.tsserver.setup{on_attach=completion_callback}
lspconfig.rust_analyzer.setup{on_attach=completion_callback}

This should give you completion. Now, we need a trigger to activate completion. I like the tab key. The config for this is available in the repo.

" Use <Tab> and <S-Tab> to navigate through popup menu
inoremap <expr> <Tab>   pumvisible() ? "\<C-n>" : "\<Tab>"
inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>"

" Set completeopt to have a better completion experience
set completeopt=menuone,noinsert,noselect

" Avoid showing message extra message when using completion
set shortmess+=c

let g:completion_enable_auto_popup = 0
imap <tab> <Plug>(completion_smart_tab)
imap <s-tab> <Plug>(completion_smart_s_tab)

Now, you can use tab and shift + tab to navigate through suggestions.