Displaying the Raspberry Pi Pico’s on-board temperature on an SPI LCD

I got my new Raspberry Pi Pico, and I’ve been very impressed by it. I can see it easily becoming my go-to board. The Pico has an on-board temperature sensor. I thought it would be a great idea to display the reading on an LCD with a SPI interface.

The code is pretty straightforward and self-explanatory:

# Display on-board temperature on a SPI LCD

import struct
import machine
import utime

def get_temp():
    sensor_temp = machine.ADC(4)
    conversion_factor = 3.3 / (65535)
    reading = sensor_temp.read_u16() * conversion_factor
    temperature = 27 - (reading - 0.706)/0.001721
    print(temperature)
    return temperature

spi_miso=machine.Pin(16) # physical pin 21
spi_sck=machine.Pin(18) # physical pin 24
spi_mosi=machine.Pin(19) # physical pin 25

rs = machine.Pin(20, machine.Pin.OUT) # physical pin 26
cs = machine.Pin(17, machine.Pin.OUT) # physical pin 22
cs.value(1)

def digitalWrite(pin, val): pin.value(1 if val else 0 )

LOW = 0
HIGH = 1


spi=machine.SPI(0,baudrate=100000,sck=spi_sck, mosi=spi_mosi, miso=spi_miso)

def sendByte(rs_val, val):
    digitalWrite(rs, rs_val);
    digitalWrite(cs, LOW);
    #spi_write_blocking(SPI_PORT, &val, 1);
    #spi.write(struct.pack('b', val))
    spi.write(struct.pack('B', val))
    digitalWrite(cs, HIGH);  
    utime.sleep(0.060);
    
contrast = 0x70  | 0b1000 #; // from 0x7C
display = 0b1111 #; // ori 0x0F
cmds = [0x39, 0x1D, 0x50, 0x6C, contrast , 0x38, display, 0x01, 0x06]
for c in cmds:
    sendByte(0, c)

msg = ["H", "E", "L", "L", "O"]
#msg = b'HELLO'
def sendStr(msg):
    for b in msg:  sendByte(1, ord(b))
    

def once():
    sendByte(0, 0x01) # clear display
    txt = "{deg:.1f}C    ".format(deg=get_temp())
    sendStr(txt)
    utime.sleep(5)
    
once()    

while True: once()
    

The code is available here.

Happy hacking!

Posted in Uncategorized | Leave a comment

No man is …

No man is good enough to govern another man without the other’s consent.

— Abraham Lincoln

Posted in Uncategorized | Leave a comment

What is history …

“What is history but the story of how politicians have squandered the blood and treasure of the human race?” ― Thomas Sowell

Posted in Uncategorized | Leave a comment

Serial output from an ATtiny85

Abstract: The serial protocol is used to display a multitude of information, and is often an indispensable aid that developers use on MCUs (microcontrollers) for debugging purposes. The ATtiny85 is a small device with a limited number of pins that has no standard serial module. This paper gives a high-level overview of the serial protocol itself, and presents code, with analysis, that can be used on an ATtiny85 to transmit data to a serial device. The code is designed to be easy to understand and conservative in its use of variable and program space. It is not aggressively optimised, though. The resulting code is less than 100 lines long, and is available online.

PDF link .

Posted in Uncategorized | Leave a comment

#rakulang Puttering about with digital signals

I’m in the process of relearning about DSP (Digital Signal Processing). alas, I’ve forgotten nearly everything that I learned initially at university. That was 30 years ago, mind.

One aspect we might like to consider is how to produce tones using MCUs. The easiest to produce is a square wave. I’m going to consider using actual MCUs at some future point, but I think it’s a little easier to explore things first using software.

Let’s use Raku to produce a simple square wave. Here’s a suitable function:

sub sq-wave($samf, $dur, $freq) {
         my $n = $samf/(2*$freq);
         my $single-wave = flat -1.0 xx $n, 1.0 xx $n;
         my $reps = $freq * $dur;
         return ($single-wave xx $reps).flat;
 }

The function takes a sampling frequency (samf), a duration of the note (dur), and the frequnecy of the note (freq). The sampling frequency represents how often we sample the input wave. I typically choose a frequency of 8KHz, because it is the default rate expected by the Linux utility aplay and is low enough that we stand a fighting chance of doing some processing on an MCU.

People, mostly confined to the very young, are potentially capable of detecting notes up to 22KHz, so there will be some notes that we cannot produce that people might be able to hear.

The function above produces amplitudes that swing backwards and forwards between -1 and +1. Suppose we want to produce a note with a frequency of 440Hz. Then we could run these commands:

 my $sample-freq = 8000;
 my $dt = 1/$sample-freq;
 my @wav1 = sq-wave($sample-freq, 10, 440);

We can save this wave using a function like:


sub save(@wav, $name, $dt) {
        my $name1 = "/tmp/$name";

        # save raw audio for playing
        my @raw = scale-array @wav, 0 , 255;
        @raw = @raw.map({$_.Int});
        my $blob = blob8.new(@raw); 
        spurt ($name1 ~ ".raw"), $blob;

        # save the first 100 samples for plotting
        my $fout = open ($name1 ~ ".dat"), :w;  
        for (0..100) -> $i {
                my $t = $i * $dt;
                $fout.print("$t @wav[$i]\n");
        }
        $fout.close();
}

Calling it is straight-forward:

save @wav1, "sqr", $dt;

This will create two files. Firstly, it will create a file called /tmp/sqr.raw . This file is suitable for playing directly by “aplay”. Just type:

aplay /tmp/sqr.dat

“save” takes the wave data, and rescales them to unsigned bytes.

The sub creates another file with an ascii format that we can pass to “gnuplot”. Only the first 101 samples are saved so as to make the plot legible. You can see a plot of the square wave in the first plot below:

“scale-array” is implemented as follows:

sub scale($x, $x-lo, $x-hi, $lo, $hi)
{
        return $lo + ($hi - $lo) * ($x - $x-lo) / ($x-hi - $x-lo);
}

sub scale-array(@arr, $lo, $hi)
{
        my $min = min @arr;
        my $max = max @arr;
        return @arr.map({scale $_, $min, $max, $lo, $hi});
}

The sound is perfectly acceptable, but we might want to go further. The discontinuities – the jumps – create high-frequencies that can sound harsh to the ears. I would like to investigate this in a further post when I hope to delve more into Fourier Analysis.

Can we smooth out this wave, bet rid of those high frequencies? The answer is “yes”. We need to apply a “low pass filter”, which is to say, a “filter” that lets the low frequencies through, whilst eliminating high frequencies.

Electronically, a low pass filter can be created using an “RC circuit”, so-called because it consists of a resistor (“R”) and a capacitor (“C”). In fact, that was exactly the solution I adopted in a previous post, where I showed how to construct a brown noise generator from white noise created by a humble ATTiny85 MCU. I still have that device. Indeed, I use it nightly, and I am satisfied with its performance. I think my design might be a little faulty, though, and could benefit from an OPAMP. That’s a discussion for another time, though.

Let’s take another tack in this post. Instead of filtering the signal electrically, let’s perform the filtering using a compute. How do we do this? Well, the simple expedient of performing numerical integration does the trick:

sub integrate(@wav, $dt) {
        my @res;
        my $y = 0;
        my $t = 0;

        #@res.append($y);
        for @wav { $t += $dt; $y += $dt* $_ ; @res.append( $y); }
        return @res;
}

We’ll want to rescale our results, too, so that the results are in the range [-1, 1]:

sub scale-integrate(@arr, $dt)
{
        return scale-array (integrate @arr, $dt), -1.0, 1.0;
}

Therefore, to integrate and save our square wave:

my @wav2 = scale-integrate @wav1, $dt;
save @wav2, "tri", $dt;

This results in a triangular wave, shown as the second plot in the diagram above.

Remember to play the wave and take note of the differences:

aplay /tmp/tri.raw

If we do a Fourier Analysis of the output, we should find that the higher frequencies are much damped-down compared to the original output. Again, that analysis will have to wait until a future occasion (not least because I need to brush up on the topic).

There’s a certain amount of “cheating” going on. Notice that the processed wave was “renormalised” to give values between -1 and -1. For now, we have the luxury of examining the whole wave and don’t have a requirement to process the signal real-time.

Doing this on a microcontroller would present a number of challenges to overcome: it must be done in real-time, the processor we choose might only have a clock frequency of 8MHz or even just 1MHz, very little memory will be available, and is highly unlikely to have an FPU (Floating Point Unit). So we would need to do our rescaling having first selected suitable range lows, highs and scaling factors.

So, we have our triangle wave. What if we wanted a sine wave? Well, that’s easy. we perform the integration once again:

my @wav3 = scale-integrate @wav2, $dt;
save @wav3, "sin", $dt;

This produces the last plot in the list, which is hard to distinguish against a true sine wave. Don’t forget to play the wave, which is all part of the fun of tinkering with this stuff:

aplay /tmp/sin.raw

So, cool, we started out with a square wave, produced a triangular wave out of it, and finally a sine (approximate) wave, no trigonometry involved. Although the ultimate outcome was to produce a sine wave, square and triangular audio waves can still be interesting in their own right.

The complete Raku code is available here. The gnuplot file that produces the plots above is available here. It is a simple file:

set multiplot layout 3,1
plot '/tmp/sqr.dat' using 1:2 pt 6
plot '/tmp/tri.dat' using 1:2 with lines
plot '/tmp/sin.dat' using 1:2 with lines
unset multiplot

that you can run by starting gnuplot and typing

load "plot.gnu"

There’s plenty of ground to develop the ideas in here further. The first is a Fourier Analysis of the three types of waves. A second would be an exploration of “noise” and their “colours”. A third would be that of simulating electrical circuits.

Lastly, we might want to investigate the effects of a high-pass filter. This would attenuate the low frequencies, and amplify the higher ones. Although I suggested that higher frequencies are “bad”, people have used them to create resonance effects that are pleasing musically.

I hope to pick up on the topic at some future time. I’d certainly need to do more reading first, though.

Anyway, I hope you guys found it interesting. It seems that the Raku folks out there are a curious lot, which is why I’ve had a bias towards writing posts which feature Raku.

As always, proof-reading has been left as an exercise for the reader.

Posted in Uncategorized | Leave a comment

#arduino and #raspberrypi componentless “level shifting”

Arduinos work on 5V, and (Raspberry) Pis work on 3V (actually 3.3V). It is OK to send a 3V signal from the Pi to the Arduino, IIRC, the Arduino registers voltage high above 2.6V, and voltage low below 2.4V.

What about the other way around? Sending 5V from the Arduino to the Pi will likely fry the Pi’s pin. So what to do?

A dubious answer would be to use a voltage divider. I’ve seen that suggested in a number of contexts, but I don’t like it. Without knowing the impedance (resistance) of the device – in this case a pin – you don’t know the overall effect it will have on the divider. You could end up with a situation where you’re sending more voltage to the pin than you realised.

The usual answer it to use a logic level shifter:

They switch between the 3.3V voltage levels of the Pi, and the 5V levels of the Arduino. That’s a lot wires all over the place, though.

I was thinking about switches the other day, and it made me wonder if there was a simple way. My idea may not be universally applicable, but it would seem an appropriate use where circumstances permit

The idea here is that the Arduino can be toggled from a floating input to a level low output. Suppose your output pin is D10. Connect a resistor (e.g. 1K) between 3V and D10. Set D10 as an input. If you connect a voltmeter between the pin and ground, it will register 3V. You have created a pull-up like you might do for reading a switch, except that you use an external rather than internal pull-up, the high voltage is 3V instead of 5V, and you’re using it as an output rather than an input.

If you programmatically switch the pin to an output pin, then this will drive your voltage low, to 0V. You therefore have a way for a 5V pin to output 3V high, and 0V low. Perfect!

Here’s a simple test circuit:

Here’s the code:

#define SW 2
#define OUT 10

void setup() {
  pinMode(SW, INPUT_PULLUP);
  pinMode(OUT, INPUT);
}

void loop() {
  if (digitalRead(SW)) {
    pinMode(OUT, INPUT);
  } else {
    pinMode(OUT, OUTPUT);
  }
  delay(10);
}

Pressing the button sends the voltage low to 0V. Releasing the button gave me a voltmeter reading of 3.23V. You can, of course, reverse the logic between high and low.

It gets even better. If you connect it to your Pi, then you set the Pi’s pin to a pulled-up input. You then don’t need resistor, as the Pi’s internal pull-up resistor will pill the voltage level to 3V. Everything works as before.

I haven’t tried it with a Pi, but I don’t see why it shouldn’t work. Can anyone see any flaws in my argument?

Posted in Uncategorized | Leave a comment

Schrödinger’s Douchebag

A guy who says offensive things and decides whether he was joking based on the reaction of people around him.Watch out, Susie! That guy is Schrödinger’s Douchebag, he’ll trap you in a thought-experiment without your consent!

That was Urban Dictionary’s word of the day for Dec 24, 2020/ Made me laugh, anyway.

Posted in Uncategorized | Leave a comment

#rustlang: my first blinky lights

I’m in the camp that reckons that there is no greater language than C++. A few years ago I tried out Rust and a few other bits and pieces like Lisp, Haskell, and so on. I sometimes play with Forth, which I consider a highly impractical language, but there’s so many cool tricks you can pull off with it.

Actually, I consider Perl an interesting language. I tried in around 2000, and didn’t much care for it. I then discovered Python, and figured that it was the way things should be done. A couple of years ago I discovered Raku (Perl 6), and it slowly grew on me. It also led me to reconsidering Perl 5 again. I actually rewrote my small accounts package in Perl 5, and I am pleased with the results. For just getting things done, Perl/Raku has a lot to recommend it.

I felt a bit hamstrung with Rust, and criticised its purpose. It seemed to have a few conveniences, but I wasn’t convinced about the whole immutability deal. My argument was: if I want something to be constant, I can just declare it const in C++. Do I really need a new language for that? Constant when I want it, mutable when I don’t.

Nim also struck me as a pretty neat language, and actually better than C++.

The problem is that there’s always a language du jour. I got fed up with the fad-chasing, and decided to standardise on C++. It is industrial strength, stable, high-level, safe (despite the naysayers) and ubiquitous. What more are you expecting?

I have been becoming much more interested in bare-metal embedded development this year. I’ve played with the usual stuff: Arduinos, Raspberry Pi, STM32 blue pills, ESP32. As a sidenote, I should point out that STM8S are great little boards, and a bit of an undiscovered secret. I think of them as souped-up ATTiny chips. If you want a dinky little MCU with low power and a generous number of pins, they really are worth checking out. They’re cheap as chips, too, to coin an old British phrase.

What rekindled my interest in Rust was when I heard that it is good at concurrency. That is something that is sorely lacking in C++ when it comes to embedded systems. Yes there are hacks that work with C/C++, but I wasn’t particularly satisfied with them.

On embedded system, C++ is really just a better C. If you’re wanting to work with bare metal Raspberry Pi development, then you don’t even have a standard C library. I have begun investigations into integrating the nano newlib library into my project. I’ve gotten malloc to work. I have a tiny printf library that I have with my project. I’m aiming to ditch that and try to use nano newlib’s library, which I fully anticipate to be more “feature-complete”. I don’t expect it to be trivial work.

It seems that newlib also offers a C++ library, which is worthy of exploration. I’m expecting that they’ll be a fair amount of effort required to get it to work, mind.

C and C++ bare metal libraries require that you provide a fair amount of foundational structure to be provided in order to get them to work. For example, when you say “stdout”, what do you mean? In Operating Systems, the meaning is clear enough. On embedded systems, well, what is it? I’ve managed to do some integration work so that there’s easy hooking-together of functionality. So if you want to make the uart as stdout, you can just initialise it as “init_uart0_as_stdio()”. I’m likely to rip out that functionality, though, in an effort to migrate to newlib.

But this leads to an obvious question: is there an ecosystem that already provides me with all this? It struck me that Rust might actually fit the bill. Folks seem to be making considerable efforts in providing a good ecosystem for embedded development.

And really, the C/C++ developers don’t seem to be doing that so much. The Arduino environment seems the best out there. It’s always popular to rag on the Arduino library, but I think they have much to offer. I think it’s better than the vendor-supplied offerings that I’ve seen. mbed, for example, I just can’t seem to get on with that.

So what’s resparked my interest in re-evaluating Rust is a potential for a well-developed ecosystem and decent concurrency mechanisms. After all, you could argue that MCUs are all about concurrency and state.

There are some intriguing ideas here. I was reading one article that had a library in Rust where tasks were run solely through interrupts. You could have your interrupt in a blocking delay. But no fear, STM32’s have nested interrupts, so you can give important tasks that must run a higher priority.

So, it has me thinking: Rust’s memory model is either a truly great idea and that non-rusters are missing the point, or else it’s a truly atrocious one. I need to investigate a little more to find out which one is the correct answer. Could it possibly be that instead of being the product of an anal retentive, it is actually a way to simplify code?

So anyway, I got my first blinky light program working, mostly as a cut-and-paste job. I’m using the stm32f1xx-hal crate. I have a vague understanding of the blinking light program, although much of it is a mystery that needs to be explored further.

Leaving aside technical issues, one danger I see is of too much fragmentation in Rust’s embedded space. This is a real danger. The bazaar method has its advantages, but it also has a kind of an “alphabet soup”. Newcomers want one good mature platform, not a dyslexic pot of letters all stirred together which they are free to rearrange as they please.

In conclusion, although my post comes across as somewhat cynical, Rustaceans (Rustafarians?) should take it as a positive. I’m willing to explore further, despite being rather mystified as to how the language works.

Source
Posted in Uncategorized | Leave a comment

Got my new Raspberry Pi 0

This is not going to be an insightful post. I’m just sharing my excitement.

I managed to fry the UART pins on my Pi 3. Since I’m working with bare metal, it’s kinda important to me that they work. I decided to get a Pi 0 for my future bare metal experiments.

I haven’t actually compiled any bare metal code for it yet. I did, however, boot my Raspbian SD card. Sure enough, everything booted and worked just fine. The boot process was a little slower, of course.

I only had a keyboard, no mouse, on account of the lack of connectivity on the Zero. So I went to a virtual console and started tapping away. The system seemed responsive. Raku was a dog to start up, mind.

I should be able to improve things by downloading the Lite version of Raspbian, and foregoing the desktop environment. It doesn’t seem necessary for what I want it for, anyway.

The Zero certainly looks suitable for a retro system. Provided you code in C/C++ and just use the console you won’t be disappointed in speed. What a fantastic piece of kit the Pi Zero is. No wonder they’re still as rare as hen’s teeth.

I haven’t tried running Firefox on it. No doubt that would be an unpleasant experience.

So, christmas has come early for me. Right, I’m off to fry some more pins.

Posted in Uncategorized | Leave a comment

#rakulang: let’s dump the partition table

I have been playing around with microcontrollers and bare-metal on Raspberry Pi 3. I am interested in reading and writing SD cards. FAT32 can be pretty complicated, so I was looking for simpler solutions. I recently discovered, for example, that Pis can’t boot on partitions greater than 32G. The firmware doesn’t understand them.

Perhaps my own solution would take the form of just raw data, a la Forth, or perhaps it would take the form of a filesystem that was much shorter and easier to program.

Now I have a problem: if I want to boot off a Pi I need a FAT32 partition, and I can’t just write into that partition if I don’t understand the filesystem. So I need two partitions. The second one is to store the data using whatever format I choose. That way, as long as I can read and write SD cards, I don’t need to bother myself with FAT32.

I could just write to blocks that I know (or assume) are beyond the FAT32 system, but I decided to see if I can decipher the MBR partition table to see where I can safely store data.

I am going to concentrate on MBR, ignore GPT, and assume that there are no extended partitions. According to osdev, partition information is stored as follows:

Partition numberOffset
Partition 10x01BE (446)
Partition 20x01CE (462)
Partition 30x01DE (478)
Partition 40x01EE (494)

The meanings of each entry is as follows:

Element (offset)SizeDescription
0byteBoot indicator bit flag: 0 = no, 0x80 = bootable (or “active”)
1byteStarting head
26 bitsStarting sector (Bits 6-7 are the upper two bits for the Starting Cylinder field.)
310 bitsStarting Cylinder
4byteSystem ID
5byteEnding Head
66 bitsEnding Sector (Bits 6-7 are the upper two bits for the ending cylinder field)
710 bitsEnding Cylinder
8uint32_tRelative Sector (to start of partition — also equals the partition’s starting LBA value)
12uint32_tTotal Sectors in partition

Thankfully, it turns out we don’t need all that Cylinder, Head, Sector stuff. The last two elements tell us all we need to know in terms of wherabouts the blocks are for the partitions. Each block is of 512 bytes.

Knowing if the partition is bootable (element 0) is not particularly interesting from my perspective, but it is easy to deal with. Knowing the SID (aka Partition type aka System Id, element 4) is useful, because I can set my own partition type for my data.

An implementation for the partition is dump is only 34 lines of code:

#!/usr/bin/env perl6

# Reference:
# https://wiki.osdev.org/Partition_Table

my %ids = 0x00 => "Empty", 0x0b => "W95 FAT32", 0x0c => "W95 FAT32 (LBA)",
	0x82 => "Linux swap", 0x83 => "Linux", 0x93 => "Amoeba";

sub MAIN($filename) {
	say "Disk: $filename";
	say "NB: addesses given in (decimal) blocks of 512 bytes each";
	say "Part  Boot    Start      End     Size   Id Type";
	my $fh = IO::Handle.new(:path($filename.IO.path)).open;
	$fh.seek(0x01BE);

	my $entry;
	sub eat() { return $entry.shift; }
	for 1..4 {
		$entry = $fh.read(16);
		my $boo = eat;
		$boo = $boo == 0x80 ?? "*" !! " ";
		for 1..3 { eat; }
		my $sid = eat;
		for 1..3 { eat; }
		my $start = $entry.read-uint32(0, LittleEndian);
		last if $start == 0;
		my $size = $entry.read-uint32(4, LittleEndian);
		my $end = $start + $size -1 ;
		my $name = %ids{$sid} || "Unknown";
		printf "%d %8.8s %8d %8d %8d 0x%.2X %s\n", $_, $boo, $start,$end, $size,  $sid, $name;

	}
	$fh.close;

It is available as a gist. (Rant: WordPress seems to be getting worse by the day, and is particularly recalcitrant to block test. I seriously need to consider moving on to something else).

The code is pretty self-explanatory, I think.

Here’s a sample output:

Disk: fall.dat
 NB: addesses given in (decimal) blocks of 512 bytes each
 Part  Boot    Start      End     Size   Id Type
 1        *        1      200      200 0x93 Amoeba
 2               201      446      246 0x9F Unknown
 3               447      646      200 0x00 Empty

What’s with the Amoeba? Well, Amoeba is a distributed OS invented by Andy Tanenbaum. It was last modified in July 1996. I figured that I would repurpose that file ID.

The Raku code doesn’t work with raw devices, it works with actual files. To create such a file, e.g. from your hard drive, issue a command like:

sudo dd if=/dev/sda of=mbr.dat bs=512 count=1

That dumps your MBR to the file mbr.dat. You only need the fist block. Then run:

perl6 pd.pl6 mbr.dat

to print it out.

Alternatively, you can make your own “disk”. This may prove useful to me if I want to experiment with reading and writing. Creating a “disk” is easy enough:

fallocate -l 20M disk.dat

This preallocated space for a file. In this case, I chose 20M, although you can set it to whatever you want.

Now edit that file:

cfdisk disk.dat

You can pretend it is a hard drive, and create partitions for it. You can take it one step further and use mkfs to format the partitions you created. I did not do that, though.

Once you’ve created partitions, you can verify our raku utility:

perl6 pd.pl6 disk.dat

Pretty neat, huh.

Rant

Whilst looking through the partition table documentation of osdev (emphasis mine):

GPT is an updated Partition Table standard … It also contains enhancements to the concept of partition tables, in general, and is significantly more complex than the MBR scheme.

As someone who likes to play around with bare metal programming on microcontrollers and Raspberry Pis, I lament the fact just how inaccessible hardware is becoming. Huge software stacks are becoming increasingly necessary to get basic peripherals working. Take keyboards, for example. Formerly, they worked through dedicated ports with fairly easy to understand interfacing. Today we have USB keyboards. The USB standard is a large, complex beast, so getting a keyboard to work is a significant undertaking.

And don’t get me started on the hideous monstrosity that is the internet.

Spleen vented.

Posted in Uncategorized | 3 Comments