Commit 5febee2b authored by Gallacchi Mattia's avatar Gallacchi Mattia
Browse files

Add build script

parent 1a5a5950
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -3,4 +3,6 @@

# Build
build/
.csv
 No newline at end of file
*.csv
*.config
*.xlsx
 No newline at end of file
+107 −1
Original line number Diff line number Diff line
@@ -8,7 +8,11 @@ This repo shows how to use the Raspberry Pi Pico SDK.
[Raspberry Pi Pico Quick Start][2]
[Raspberry Pi Pico W (WiFi) Quick start][3]

## Toolchain
## Tools

**This guide as been tested for Ubuntu 22.04**

### Toolchain

A docker image containing all the tools is available. You can build it with the following command.

@@ -32,6 +36,108 @@ or
docker run --rm -it --name pico-env -v $PWD:/mnt/ pico-dev
```

### Flash and debug

You can use the Raspberry Pi Debug probe to flash and debug applications. To use it you must first install OpenOCD.
Download and install the OpenOCD dependencies:

```bash
sudo apt install automake autoconf build-essential texinfo libtool libftdi-dev libusb-1.0-0-dev
```

Clone, build and install OpenOCD:

```bash
cd ~
git clone https://github.com/raspberrypi/openocd.git --branch rp2040-v0.12.0 --depth=1 --no-single-branch
cd openocd
./bootstrap
./configure
make -j4
sudo make install
```

Check OpenOCD version:

```bash
openocd --version
```

Output: 

```bash
Open On-Chip Debugger 0.12.0-g4d87f6d (2024-01-08-14:31)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html

```

Create a file with any name (ex. pico_openocd.rules) under /etc/udev/rules.d and put this line inside.

```bash
ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000c", MODE="660", GROUP="plugdev", TAG+="uaccess"
```

## Compile Pico W code

Run the docker container and navigate to the *sht30/pico_w* folder. Create a *build* folder and navigate to it:

```bash
mkdir build && cd build
```

Inside the *build* folder you will setup the **CMake** environnement.

```bash
cmake -DPICO_BOARD=grove_pico_w ..
```

This will initialize **CMake** and generate a Makefile. To build the project simply run:

```bash
make
```

Alternatively you can run the *build.sh* script from the top folder. 

1. Setup project folder and board type (run only once)

```bash
./build.sh -p sht30/pico_w/ -t grove_pico_w
```

2. Compile

```bash
./build.sh -b
```

## Deploy application on the Raspberry Pi Pico

### Flash using UF2 (USB Flashing Format)

You can flash the Pico via USB using the *.uf2* file.

1. Power up the Pico while pressing the **BOOTSEL** button
2. You Pico should show up as a drive.
3. Copy the *pico_w/build/sht30.uf2* onto the Pico.
    1. Linux:   
        ```bash
        cp pico_w/build/sht30.uf2 /media/$USER/RPI-RP2
        ```
    2. Windows: Drag and drop the file

### Flash using debug probe and OpenOCD

Run the build script with the *-f* option:

```bash
./build.sh -f
```

> :warning: This command must be performed on your host machine not in a running docker container

## Projects

[SHT30 sensor](sht30/README.md)

build.sh

0 → 100755
+139 −0
Original line number Diff line number Diff line
#!/bin/bash

SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"

BUILD_CONFIG=build.config
PROJECT_PATH=""
BOARD_TYPE=""
OPENOCD="/usr/local/bin/openocd"
OPENOCD_PATH="/usr/local/share/openocd/scripts"
OPENOCD_ADAPTER=cmsis-dap

help()
{
    echo "Build Raspberry Pi Pico projects"
    echo
    echo "Syntax: $0 [-b|c|d|f|h|p|t] <args>"
    echo "[ -b ]        Build project."
    echo "[ -c ]        Clean"
    echo "[ -d ]        Launch debugger"
    echo "[ -f ]        Flash board"
    echo "[ -h ]        Help"
    echo "[ -p PATH ]   Set project path"
    echo "[ -t BOARD ]  Set board type"
}

init_config()
{
if [ ! -f $BUILD_CONFIG ]; then
    touch $BUILD_CONFIG
    echo "PROJECT_PATH=\"\"" >> $BUILD_CONFIG
    echo "BOARD_TYPE=\"\"" >> $BUILD_CONFIG
fi
}

build=0
clean=0
debug=0
flash=0

if [ $# -eq 0 ]; then
    echo "Error: missing arguments"
    help
    exit 0
else
    while getopts "bcdfhp:t:" opt; do
        case $opt in
            b)
                build=1
                ;;
            c)
                clean=1
                ;;
            d)
                debug=1
                ;;
            
            f)
                flash=1
                ;;
            h)
                help
                exit 1
                ;;
            p)
                PROJECT_PATH=${OPTARG}

                if [ ! -d $PROJECT_PATH ]; then
                    echo "${PROJECT_PATH} does not exist"
                    exit 1
                fi

                init_config

                sed -i "1s#.*#PROJECT_PATH=$PROJECT_PATH#" $BUILD_CONFIG
                ;;

            t)
                BOARD_TYPE=${OPTARG}

                init_config

                sed -i "2s/.*/BOARD_TYPE=$BOARD_TYPE/" $BUILD_CONFIG
                ;;

            \?)
                help
                exit 1
                ;;
            :)
                echo "Option -$OPTARG requires an argument." >&2
                exit 1
                ;;
        esac
    done
fi

# Check if config file exists
if [ ! -f $BUILD_CONFIG ]; then
    echo "Error no config file found. Please run $0 -p <project_path> -t <board_type>"
    exit 1
else
    # Read config file
    source $BUILD_CONFIG
    
    if [ "${PROJECT_PATH}" == "" ]; then
        echo "Error: Project path not found"
        exit 1
    fi

    if [ "${BOARD_TYPE}" == "" ]; then
        echo "Error: Board type not found"
        exit 1
    fi
fi

if [ $clean == 1 ]; then
    cd ${PROJECT_PATH}build
    make clean
fi

if [ $build == 1 ]; then
    echo "Building project ${PROJECT_PATH}"
    cd $PROJECT_PATH
    mkdir -p build && cd build
    cmake cmake -DPICO_BOARD=$BOARD_TYPE ..
    make
    cd $SCRIPTPATH
fi

if [ $flash == 1 ]; then
    # Find program
    PROGRAM="$(find ${PROJECT_PATH}build -maxdepth 1 -type f -name "*.elf")"
    echo "Flashing ${PROGRAM}"
    sudo openocd -f interface/${OPENOCD_ADAPTER}.cfg -f target/rp2040.cfg -c "adapter speed 5000" -c "program ${PROGRAM} verify reset exit"
fi

if [ $debug == 1 ]; then
    sudo openocd -f interface/${OPENOCD_ADAPTER}.cfg -f target/rp2040.cfg -c "adapter speed 5000"
fi
 No newline at end of file
+16 −42
Original line number Diff line number Diff line
@@ -18,8 +18,22 @@ Once connected to the network it will start a loop that send UDP packages at reg
| Pico IP | 192.168.1.102 |
| UDP port | 4444 |

A led flashes to indicate that everything is running correctly. If it doesn't (either always on or off) 
something is wrong.
A led flashes every 0.5s to indicate that everything is running correctly. If an error occurred the led will flash with a pattern.
This pattern is a number of flashes every 0.25s and a pause of 2s. The table below shows the meaning of each pattern.

| No. of flashes | Error description |
|----------------|-------------------|
| 1 | Failed to connect to WiFi network |
| 2 | Failed to connect the SHT30 sensor |
| 3 | Failed to setup timer |
| 4 | Failed to take a measure |

## Build script setup

| Configuration | Command |
|---------------|---------|
| Project path | ./build.sh -p sht30/pico_w | 
| Board type | ./build.sh -t grove_pico_w | 

## Hardware connection

@@ -29,46 +43,6 @@ You can mount the Pico on a [Grove Basic Kit for Raspberry Pi Pico][1] and conne

![Grove shield image](img/grove_shield_sht30.drawio.png)


## Compile Pico W code

Run the docker container and navigate to the *sht30/pico_w* folder. Create a *build* folder and navigate to it:

```bash
mkdir build && cd build
```

Inside the *build* folder you will setup the **Cmake** environnement.

```bash
cmake -DPICO_BOARD=grove_pico_w ..
```

This will initialize **CMake** and generate a Makefile. To build the project simply run:

```bash
make
```

## Deploy code on the Raspberry Pi Pico

### Deploy UF2 (USB Flashing Format)

You can flash the Pico via USB using the *.uf2* file.

1. Power up the Pico while pressing the **BOOTSEL** button
2. You Pico should show up as a drive.
3. Copy the *pico_w/build/sht30.uf2* onto the Pico.
    1. Linux:   
        ```bash
        cp pico_w/build/sht30.uf2 /media/$USER/RPI-RP2
        ```
    2. Windows: Drag and drop the file

### Deploy flash

TODO

## UDP client 

The *python* folder contains a simple UDP client that store received values in CSV file. To use it connect to
+79 −24
Original line number Diff line number Diff line
@@ -22,6 +22,26 @@
#define BEACON_MSG_LEN_MAX      127
#define BEACON_TARGET           "255.255.255.255"
#define BEACON_INTERVAL_MS      2500
#define CONNECT_TIMEOUT         10000 // 10s

enum ERROR_CODES {
    NONE=0,
    WIFI_CONNECT,
    SHT30_CONNECT,
    TIMER_SETUP,
    UDP_LOOP,
};

bool flash_led_callback(struct repeating_timer *t)
{
    static bool led = true;

    // Flash the led to show activity
    cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led);
    led = !led;

    return true;
}

bool run_udp_beacon(SHT30 *sensor)
{
@@ -41,7 +61,7 @@ bool run_udp_beacon(SHT30 *sensor)
            error = true;
        }   

        snprintf(req, BEACON_MSG_LEN_MAX, "%f:%f\n", mes.temperature, mes.humidity);
        snprintf(req, BEACON_MSG_LEN_MAX, "%04X:%0.2f;%04X:%0.2f\n",mes.raw_temperature, mes.temperature, mes.raw_humidity, mes.humidity);
        err_t er = udp_sendto(pcb, p, &addr, UDP_PORT);
        pbuf_free(p);
        if (er != ERR_OK) {
@@ -50,10 +70,6 @@ bool run_udp_beacon(SHT30 *sensor)
            printf("Sent packet %f:%f\n", mes.temperature, mes.humidity);
        }

        // Flash the led to show activity
        led = !led;
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led);

        // Note in practice for this simple UDP transmitter,
        // the end result for both background and poll is the same

@@ -73,40 +89,78 @@ bool run_udp_beacon(SHT30 *sensor)
    return error;
}

void led_errors(ERROR_CODES code)
{
    uint32_t interval = 250;
    uint32_t pause = 2000;
    uint8_t flash_count = (uint8_t)code;

    cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);

    while(1) {
        for(uint8_t i = 0; i < flash_count; i++) {
            cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
            sleep_ms(interval);
            cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
            sleep_ms(interval);
        }

        sleep_ms(pause);
    }
}

int main()
{
    struct repeating_timer timer;
    struct repeating_timer *timer = new struct repeating_timer;
    SHT30 *sht;
    ERROR_CODES err = NONE;

    stdio_init_all();

    // WiFi init
    /** WiFi init **/
    printf("\n*** Init WiFi ***\n");    
    if (cyw43_arch_init()) {
        printf("failed to initialise\n");
        return 1;
        while(1) {
            printf("Failed to initialise WiFi\n");
            sleep_ms(1000);
        }
    }

    cyw43_arch_enable_sta_mode();

    if(!add_repeating_timer_ms(500, flash_led_callback, NULL, timer)) {
        printf("Error: Failed to add timer\n");
        err = TIMER_SETUP;
        goto end;
    }

    printf("Connecting to Wi-Fi...\n");
    if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) {
        printf("failed to connect.\n");
    if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, CONNECT_TIMEOUT)) {
        printf("Error: Failed to connect to %s.\n", WIFI_SSID);
        err = WIFI_CONNECT;
        goto end;
    } else {
        printf("Connected.\n");
    }
    printf("Connected to %s.\n", WIFI_SSID);

    /**
     * Init SHT30 sensor
    */
    printf("\n*** Init SHT30 sensor ***\n");
    sht = new SHT30();

    if (!sht->is_connected()) {
        printf("Error: SHT30 is not connected\n");
        return -1;
        printf("Error: Failed to connect SHT30\n");
        err = SHT30_CONNECT;
        delete sht;
        goto end;
    }
    printf("SHT30 connected\n");

    run_udp_beacon(sht);
    if(!run_udp_beacon(sht)) {
        printf("Error: Failed to start UDP loop");
        err = UDP_LOOP;
        goto end;
    }

    printf("An error occurred stopping\n");

@@ -115,7 +169,8 @@ int main()
        printf("Status reg: 0x%04X\n", status_reg);

    end:
        cyw43_arch_deinit();
        printf("Exiting\n");
        if (timer->alarm_id > 0)
            cancel_repeating_timer(timer);
        led_errors(err);
        return 1;
}
 No newline at end of file
Loading