Pico SDK on OSX

Before starting to use Swift to build embedded software, it is essential to install the native SDK for the Raspberry Pico so that we have a basic toolchain installed. In the next post, we’ll see how to use Swift.

In this post we’ll learn how to:

  • Install the Pico SDK
  • Run a native example

Install the Pico SDK

First step:

cd ~
mkdir pico
cd pico

Second step:

git clone -b master https://github.com/raspberrypi/pico-sdk.git

# Set the PICO_SDK_PATH environment variable to where you just cloned the repo.
export PICO_SDK_PATH=/path/to/pico-sdk

cd pico-sdk
git submodule update --init
cd ..
git clone -b master https://github.com/raspberrypi/pico-examples.git

Third step:

# Install cmake
brew install cmake

# Install the arm eabi toolchain
brew install --cask gcc-arm-embedded

xcode-select --install

Fourth step:

git clone https://github.com/pimoroni/pimoroni-pico.git
cd pimoroni-pico
git submodule update --init
mkdir build

Fifth step:

cd build
cmake ..
make

Run a tative example

Create a directory for the project, in this directory create this main.c

#include <stdio.h>
#include "pico/stdlib.h"

int main() {

    const uint led_pin = 25;

    // Initialize LED pin
    gpio_init(led_pin);
    gpio_set_dir(led_pin, GPIO_OUT);

    // Initialize chosen serial port
    stdio_init_all();

    // Loop forever
    while (true) {

        // Blink LED
        printf("Blinking!\r\n");
        gpio_put(led_pin, true);
        sleep_ms(1000);
        gpio_put(led_pin, false);
        sleep_ms(1000);
    }
}

Now the CMakeLists.txt

# Set minimum required version of CMake
cmake_minimum_required(VERSION 3.12)

# Include build functions from Pico SDK
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)

# Set name of project (as PROJECT_NAME) and C/C   standards
project(blink C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

# Creates a pico-sdk subdirectory in our project for the libraries
pico_sdk_init()

# Tell CMake where to find the executable source file
add_executable(${PROJECT_NAME}
    main.c
)

# Create map/bin/hex/uf2 files
pico_add_extra_outputs(${PROJECT_NAME})

# Link to pico_stdlib (gpio, time, etc. functions)
target_link_libraries(${PROJECT_NAME}
    pico_stdlib
)

# Enable usb output, disable uart output
pico_enable_stdio_usb(${PROJECT_NAME} 1)
pico_enable_stdio_uart(${PROJECT_NAME} 0)

To compile:

mkdir build
cd build
cmake ..
make

After that, we should have the file blink.uf2 in the build directory. Connect your Pico to the computer, open it like a pendrive, and copy the file. Reboot the Pico; it should now blink.

External source that i used for this post:

https://www.digikey.com/en/maker/projects/raspberry-pi-pico-and-rp2040-cc-part-1-blink-and-vs-code/7102fb8bca95452e9df6150f39ae8422

https://forums.raspberrypi.com/viewtopic.php?t=357243

https://github.com/pimoroni/pimoroni-pico/blob/main/setting-up-the-pico-sdk.md

Architecture Foundamentals

I want to suggest this book: “Fundamentals of Software Architecture: An Engineering Approach” (https://tinyurl.com/35ewyab4) for both newbies and expert engineers. What do I like about this book?

  • Very clear about the architecture properties.
  • For every architecture, at the end of the chapter, we find a card with the strengths and weaknesses.
  • Advice for tools (drawing and other).
  • Considers the soft skills for an architect, such as how to communicate with each other (for example, how to write an email to communicate an architectural decision). It puts humans at the center, not just the technology.
  • How to build an architecture career.
  • How to stay updated in this field.

Need anything else?

Share SwiftData with a Widget

In this post, we’ll see how to share data from the application to the widget.

First, create the project and call it SharedWidget, selecting SwiftData as storage. After that, create the widget by selecting Target in the File -> New menu and then WidgetExtension.

After the creation process, you can also deselect the Live and Intent options because we won’t be using them.

To share data between the application and the widget, we need to create an App Group in the capabilities. After that, add a group and call it whatever you want. In my case, I named it:

Then, select the widget extension in the target:

Thus, also for it, add the app group in the capabilities, but this time don’t add a new one; select the existing one.

One last thing before we look at the code. We have to share the model between the app and the widget, so select the Item.swift file and select both project components:

If you run the application at this moment, you can add an item (the default Apple example for SwiftData) and if you add the widget, you’ll see a time and an emoji. We want to simply display the number of items added below the emoji, so the first thing to do is:

struct SimpleEntry: TimelineEntry {
    let date: Date
    let emoji: String
    let number: Int
}

Where the TimeLineEntry is: “A type that specifies the date to display a widget, and, optionally, indicates the current relevance of the widget’s content.”

Now, in the provider, we add the structure to retrieve the item numbers:

@MainActor
    private func getNumberOfItems() -> Int {
        guard let modelContainer = try? ModelContainer(for: Item.self) else {
            return 0
        }
        let descriptor = FetchDescriptor<Item>()
        let number = try? modelContainer.mainContext.fetch(descriptor).count
        return number ?? 0
    }

Note the @MainActor, now we change the Provider in this way:

struct Provider: TimelineProvider {
    
    @MainActor
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), emoji: "😀", number: getNumberOfItems())
    }

    @MainActor
    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date(), emoji: "😀", number: getNumberOfItems())
        completion(entry)
    }

    @MainActor
    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        print(Date())
        let timeline = Timeline(entries: [SimpleEntry(date: Date(), emoji: "😀", number: getNumberOfItems())], policy: .after(.now.advanced(by: 60)))
        completion(timeline)
    }
    
    @MainActor
    private func getNumberOfItems() -> Int {
        guard let modelContainer = try? ModelContainer(for: Item.self) else {
            return 0
        }
        let descriptor = FetchDescriptor<Item>()
        let number = try? modelContainer.mainContext.fetch(descriptor).count
        return number ?? 0
    }
}

So, every function that uses the getNumberOfItems must use the @MainActor annotation (Its role is to ensure that all code is executed on the main thread).

For more information about timelines, I advise reading the official documentation: Apple Developer – Timeline.

Now do a little changes in the view:

struct ForShareWidgetEntryView : View {
    var entry: Provider.Entry
    
    var body: some View {
        VStack {
            Text("Time:")
            Text(entry.date, style: .time)

            Text("Emoji:")
            Text(entry.emoji)
            Text("\(entry.number)")
        }
    }
}

Now everything should work. Keep in mind that in a production environment, the operating system decides when to update the widget.

Swift Embedded

Until now, we have been able to use Swift to create applications for:

  • iPhone, iPad, Apple computers, Apple TV, Apple Watch
  • Servers with Vapor
  • Android using skip.tools

As of today, it is also possible to use Swift to create applications on embedded hardware (like Raspberry Pi Pico, Arduino, and so on).

Currently, the Swift Embedded version is in a preview status (you can find more information here), but it has the potential to become a real alternative to C/C++. Sure, at the moment, languages like Rust have an advantage in this area (not to forget MicroPython), but it’s a game worth playing.

Chart in SwiftUI

Charts are not present in every mobile app, but they are quite common in sports, health, and finance applications. In this post, we’ll learn how to:

  • Create a chart (bar and line)
  • Set the unit
  • Improve the visibility of the information on the bars

Create a chart

First create the structure of the data:

struct MoneyData: Identifiable {
    let id = UUID()
    let operation: Int
    let euro: Double
}

Then, add a chart to the View (note the import Charts):

import SwiftUI
import Charts

struct ContentView: View {
    @State var expences: [MoneyData] = [MoneyData]()
    var body: some View {
        VStack {
            Chart(expences, id: \.id) { ex in
                BarMark(
                    x: .value("Operation", ex.operation),
                    y: .value("Money", ex.euro)
                )
            }
            .frame(height: 300)
            .padding()
            .onAppear {
                self.expences = initMoney()
            }
        }
    }
    func initMoney() -> [MoneyData] {
        return [MoneyData(operation: 1, euro: 2.5),
                MoneyData(operation: 2, euro: 6.5), MoneyData(operation: 3, euro: 1.5)
        ]
    }
}

To have:

Next, the chart iterates over the expenses array, displaying the operation on the X-axis and the euros on the Y-axis.

To create a linear chart, use LineMark instead of BarMark:

LineMark(
         x: .value("Operation", ex.operation),
         y: .value("Money", ex.euro)
)

To have:

Note that the last operation has three values but is not displayed. To avoid this behavior, we need to add one value at the beginning and one at the end of the X-axis. Let’s see how to do this:

struct ContentView: View {
    @State var expences: [MoneyData] = [MoneyData]()
    @State var gridValue = [Int]()
    
    var body: some View {
        VStack {
            Chart(expences, id: \.id) { ex in
                LineMark(
                    x: .value("Operation", ex.operation),
                    y: .value("Money", ex.euro)
                )
            }.chartXAxis {
                AxisMarks(values: gridValue)  
            }
            .frame(height: 300)
            .padding()
            .onAppear {
                self.expences = initMoney()
                self.gridValue = Array((expences[0].operation - 1)...(expences[expences.count - 1].operation + 1))
            }
        }
    }
    func initMoney() -> [MoneyData] {
        return [MoneyData(operation: 1, euro: 2.5),
                MoneyData(operation: 2, euro: 6.5), MoneyData(operation: 3, euro: 1.5)
                
        ]
    }
}

First, we add a State variable gridValue, initialized in the onAppear method with an interval for the operation that is larger than the interval in the money data (e.g., [0,3] instead of [1,2]).

This interval is used by applying:

.chartXAxis {
                AxisMarks(values: gridValue)  
            }

Thus, we have:

Unit

Now, let’s see what happens if we use dates on the X-axis.

To use dates, first, adjust the data structure to include dates.

struct RunData: Identifiable {
    let id = UUID()
    let date: Date
    let km: Double
}

Then make some small changes in the code to use this type of data with the chart:

struct ContentView: View {
    @State var races: [RunData] = [RunData]()
    
    var body: some View {
        VStack {
            Chart(races, id: \.id) { run in
                BarMark(
                    x: .value("Date", run.date),
                    y: .value("Km", run.km)
                )
            }
            .frame(height: 300)
            .padding()
            .onAppear {
                self.races = initData()
            }
        }
    }
    func initData() -> [RunData] {
        let dateFormatter = ISO8601DateFormatter()
        dateFormatter.formatOptions = [.withFullDate]
        
        return [RunData(date: dateFormatter.date(from: "2024-05-03") ?? Date.now, km: 15),
                RunData(date: dateFormatter.date(from: "2024-05-05") ?? Date.now, km: 20),
                RunData(date: dateFormatter.date(from: "2024-05-07") ?? Date.now, km: 10)
        ]
    }
}

Simply, we replaced the moneyData with runData and used a different initialization function, just to have:

As you can see, we only have the label for “5 May.” Don’t worry, we have a simpler solution that I showed before. We can add the unit value for the X-axis:

BarMark(
         x: .value("Date", run.date, unit: .day),
         y: .value("Km", run.km)
)

Now we see:

Now you should have the knowledge base to implement charts in your app.

Note: English is not my native language, so I apologize for any errors. I use AI solely to generate the banner of the post; the content is human-generated.