prettyprint

2023年11月22日 星期三

Raspberry Pi || Node-RED || Pico W || TLS: Ep2. MQTT (mosquitto) 2wayauth

 本文章介紹在Raspberry Pi 3B+上建立Mosquitto MQTT Broker。並在Node-RED 與Pico W上互相Publish 與subscribe message。mosquitto 設定需要user name與password。並啟用TLS 2 way authentication功能。

有關Mosquitto MQTT Broker詳細設定步驟請參閱「MQTT 簡介:使用Mosquitto Broker」文章介紹。

本專案詳細設定步驟請參閱下列影片。

成果影片:





程式碼:

主程式:

  • picow_tls_mqtt.c


#include "stdio.h"
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include "picow_tls_mqtt.h"
#include "picow_tls_cert.h"
#include "lwip/apps/mqtt.h"
#include "lwip/altcp.h"

#include "pio_rotary_encoder.h"
#include "ws2812.h"

#define WS2812_TOTAL_PIXELS 12
#define WIFI_SSID           "your-ssid"
#define WIFI_PASSWORD       "your-password"
#define SERVER_IP           "your-mqtt-broker-ip"

static mqtt_client_instance_t mqtt_client;
static int8_t rotary_value=0;
static uint32_t ws2812_color=0;
uint8_t pub_buff[1024];

void update_ws2812_color() {
    for (int i=0; i < rotary_value; i++) {
      ws2812_put_pixel_rgb(ws2812_color);
    }
    for (int i=rotary_value; i < WS2812_TOTAL_PIXELS; i++) {
      ws2812_put_pixel_rgb(0x0);
    }
}

void mqtt_incoming_data_cb(void *arg, const u8_t *data, u16_t len, u8_t flags)
{
  mqtt_client_instance_t* client_inst = (mqtt_client_instance_t*)arg;
  LWIP_UNUSED_ARG(data);
  memcpy(client_inst->sub_message+client_inst->sub_message_offset, data, len);
  client_inst->sub_message_offset += len;
  if (flags == MQTT_DATA_FLAG_LAST) {
      client_inst->sub_message_recved = true;
      client_inst->sub_message[client_inst->sub_message_offset]=0;
      ws2812_color = strtol(client_inst->sub_message, NULL, 16);
      update_ws2812_color();
  }

}

void mqtt_incoming_publish_cb(void *arg, const char *topic, u32_t tot_len)
{
    mqtt_client_instance_t* client_inst = (mqtt_client_instance_t*)arg;
    client_inst->sub_message_offset=0;
    client_inst->sub_message_recved=false;
    strcpy(client_inst->sub_topic, topic);
}

void mqtt_request_cb(void *arg, err_t err)
{
  mqtt_client_instance_t* client_inst = (mqtt_client_instance_t*)arg;
  client_inst->connected=true;
  //LWIP_PLATFORM_DIAG(("MQTT client \"%s\" request cb: err %d\n", client_inst->mqtt_connect_client_info.client_id, (int)err));
}

void mqtt_pub_request_cb(void *arg, err_t err)
{
  mqtt_client_instance_t* client_inst = (mqtt_client_instance_t*)arg;
  //LWIP_PLATFORM_DIAG(("MQTT client \"%s\" pub request cb: err %d\n", client_inst->mqtt_connect_client_info.client_id, (int)err));
}

void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
{
    mqtt_client_instance_t* client_inst = (mqtt_client_instance_t*)arg;
    if (status == MQTT_CONNECT_ACCEPTED) {
      mqtt_sub_unsub(client,
            "ws2812_color", 1,
            mqtt_request_cb, LWIP_CONST_CAST(void*, client_inst),
            1);
      
    }
}

void rotary_encoder_cb(uint8_t action) {
  if (action == PIO_RE_CLOCKWISE || action == PIO_RE_COUNTERCLOCKWISE) {
    if (action == PIO_RE_CLOCKWISE) {
      rotary_value -= 1;
      if (rotary_value < 0) rotary_value=0;
    }
    if (action == PIO_RE_COUNTERCLOCKWISE) {
      rotary_value += 1;
      if (rotary_value > 12) rotary_value=12;
    }
  }
}

int main()
{
    int wifi_stat;
    err_t mqtt_stat;
    stdio_init_all();
    if (cyw43_arch_init()) {
        printf("cyw43 init error\n");
        return 0;
    }
    pio_rotary_encoder_default_init();
    pio_rotary_encoder_set_callback(rotary_encoder_cb);
    ws2812_default_pio_init();

    for (int i=0; i < WS2812_TOTAL_PIXELS; i++) ws2812_put_pixel(0x0);
    sleep_ms(50);
    //led: red
    ws2812_put_pixel_rgb(0xff0000);

    // connect to WiFi
    cyw43_arch_enable_sta_mode();
    wifi_stat = cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000);
    if (wifi_stat) {
        printf("wifi connect error:%d\n", wifi_stat);
        return 0;
    }
    printf("Wifi connected\n");
    //led: blue
    ws2812_put_pixel_rgb(0xff);

    //mqtt_client.tls_config = altcp_tls_create_config_client(ca_cert, sizeof(ca_cert));  
    mqtt_client.tls_config = altcp_tls_create_config_client_2wayauth(ca_cert, sizeof(ca_cert), mqtt_client_key, sizeof(mqtt_client_key), NULL, 0, mqtt_client_cert, sizeof(mqtt_client_cert));
    assert(mqtt_client.tls_config);
 
    /*
    // if DNS query
    mbedtls_ssl_set_hostname(altcp_tls_context(tls_client->tpcb), SERVER_NAME);
    tls_client->dns_found=false;
    cyw43_arch_lwip_begin();
    
    tcp_stat = dns_gethostbyname(SERVER_NAME, &tls_client->server_addr, tls_client_dns_found, tls_client);     
    if (tcp_stat == ERR_OK) printf("dns err ok\n");
    if (tcp_stat == ERR_INPROGRESS) printf("dns inprogress...\n");
    while(!tls_client->dns_found && tcp_stat != ERR_OK) {
         cyw43_arch_poll();
        sleep_ms(10);
    }

    */
    // if not by dns
    ipaddr_aton(SERVER_IP, &mqtt_client.ipaddr);
   
    // connect to mqtt server;
    mqtt_client.mqtt_connect_client_info = (struct mqtt_connect_client_info_t){
      "clientid", //client_id;
      "username", //client_user;
      "password", //client_pass;
      100,//keep_alive;
      NULL, // will_topic;
      NULL, // will_msg;
      0, //will_qos;
      0,// will_retain;
#if LWIP_ALTCP && LWIP_ALTCP_TLS
      mqtt_client.tls_config //tls_config;
#endif
    };

    mqtt_client.client = mqtt_client_new();
    //mbedtls_ssl_set_hostname(altcp_tls_context(conn)), SERVER_NAME);

    mqtt_set_inpub_callback(mqtt_client.client,
          mqtt_incoming_publish_cb,
          mqtt_incoming_data_cb,
          LWIP_CONST_CAST(void*, &mqtt_client.mqtt_connect_client_info));
    mqtt_client.connected=false;
    cyw43_arch_lwip_begin();
    mqtt_stat = mqtt_client_connect(mqtt_client.client,
          &mqtt_client.ipaddr, LWIP_IANA_PORT_SECURE_MQTT,
          mqtt_connection_cb, LWIP_CONST_CAST(void*, &mqtt_client),
          &mqtt_client.mqtt_connect_client_info);
    cyw43_arch_lwip_end();
   
    absolute_time_t timeout = make_timeout_time_ms(10000);
    while(!mqtt_client.connected && absolute_time_diff_us(get_absolute_time(), timeout) > 0) {
        sleep_ms(10);
        cyw43_arch_poll();
        printf(".");
    }
    printf("\n");
    if (!mqtt_client.connected) {
      printf("connect mqtt server:timeout\n");
      return 0;
    }
    
    printf("mqtt connected\n");
    // led : green
    ws2812_put_pixel_rgb(0x00ff00);
    ws2812_color = 0x00ff00;
    rotary_value = 1;
   
    sprintf(pub_buff, "%02d", rotary_value);
    mqtt_publish(mqtt_client.client, "ROTARY", pub_buff, 2, 1, 0, mqtt_pub_request_cb, &mqtt_client);

    uint8_t pre_rotary_value=rotary_value;
    uint32_t pre_ws2812_color=ws2812_color;
    
    while(1) {

        cyw43_arch_poll();
        sleep_ms(10);
        if (pre_rotary_value != rotary_value) {
          sprintf(pub_buff, "%02d", rotary_value);
          mqtt_publish(mqtt_client.client, "ROTARY", pub_buff, 2, 1, 0, mqtt_pub_request_cb, &mqtt_client);
          update_ws2812_color();
          pre_rotary_value = rotary_value;
        }
    }
    cyw43_arch_deinit();
    return 0;
}

  • picow_tls_mqtt.h


#ifndef __PICOW_TLS_CLIENT_H__
#define __PICOW_TLS_CLIENT_H__
#include "lwip/pbuf.h"
#include "lwip/altcp_tcp.h"
#include "lwip/altcp_tcp.h"
#include "lwip/altcp_tls.h"
#include "lwip/dns.h"
#include "lwip/apps/mqtt.h"
#define RECV_BUFF_MAX_LEN   1024

typedef struct {
    mqtt_client_t *client;
    struct altcp_tls_config *tls_config;
    ip_addr_t ipaddr;
    struct mqtt_connect_client_info_t mqtt_connect_client_info;
    bool connected;
    uint8_t sub_topic[256];
    uint8_t sub_message[RECV_BUFF_MAX_LEN];
    uint16_t sub_message_offset;
    bool sub_message_recved;
} mqtt_client_instance_t;

#endif

  • picow_tls_cert.h


#define ca_cert "-----BEGIN CERTIFICATE-----\n\
...
-----END CERTIFICATE-----\n"

#define mqtt_client_cert "-----BEGIN CERTIFICATE-----\n\
...
-----END CERTIFICATE-----"

#define mqtt_client_key "-----BEGIN RSA PRIVATE KEY-----\n\
...
-----END RSA PRIVATE KEY-----"

  • CMakeLists.txt


# Generated Cmake Pico project file

cmake_minimum_required(VERSION 3.13)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "/home/duser/pico/pico-sdk")

set(PICO_BOARD pico_w CACHE STRING "Board type")

# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)

if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0")
  message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()

project(picow_tls_mqtt C CXX ASM)

# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()

# Add executable. Default name is the project name, version 0.1

add_executable(picow_tls_mqtt picow_tls_mqtt.c)

pico_set_program_name(picow_tls_mqtt "picow_tls_client")
pico_set_program_version(picow_tls_mqtt "0.1")

pico_enable_stdio_uart(picow_tls_mqtt 1)
pico_enable_stdio_usb(picow_tls_mqtt 0)

# Add the standard library to the build
target_link_libraries(picow_tls_mqtt
        pico_stdlib)

# Add the standard include files to the build
target_include_directories(picow_tls_mqtt PRIVATE
  ${CMAKE_CURRENT_LIST_DIR}
  ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
)

# Add any user requested libraries
target_link_libraries(picow_tls_mqtt 
        pico_cyw43_arch_lwip_poll
        pico_lwip_mqtt
        pico_lwip_mbedtls
        pico_mbedtls
        )
add_subdirectory(rotary_encoder) 
target_link_libraries(picow_tls_mqtt
  rotary_encoder
)

add_subdirectory(ws2812) 
target_link_libraries(picow_tls_mqtt
ws2812
)

pico_add_extra_outputs(picow_tls_mqtt)

  • lwipopts.h


#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__


// Common settings used in most of the pico_w examples
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)

// allow override in some examples
#ifndef NO_SYS
#define NO_SYS                      1
#endif
// allow override in some examples
#ifndef LWIP_SOCKET
#define LWIP_SOCKET                 0
#endif
#if PICO_CYW43_ARCH_POLL
#define MEM_LIBC_MALLOC             1
#else
// MEM_LIBC_MALLOC is incompatible with non polling versions
#define MEM_LIBC_MALLOC             0
#endif
#define MEM_ALIGNMENT               4
#define MEM_SIZE                    4000
#define MEMP_NUM_TCP_SEG            32
#define MEMP_NUM_ARP_QUEUE          10
#define PBUF_POOL_SIZE              24
#define LWIP_ARP                    1
#define LWIP_ETHERNET               1
#define LWIP_ICMP                   1
#define LWIP_RAW                    1
#define TCP_WND                     (8 * TCP_MSS)
#define TCP_MSS                     1460
#define TCP_SND_BUF                 (8 * TCP_MSS)
#define TCP_SND_QUEUELEN            ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
#define LWIP_NETIF_STATUS_CALLBACK  1
#define LWIP_NETIF_LINK_CALLBACK    1
#define LWIP_NETIF_HOSTNAME         1
#define LWIP_NETCONN                0
#define MEM_STATS                   0
#define SYS_STATS                   0
#define MEMP_STATS                  0
#define LINK_STATS                  0
// #define ETH_PAD_SIZE                2
#define LWIP_CHKSUM_ALGORITHM       3
#define LWIP_DHCP                   1
#define LWIP_IPV4                   1
#define LWIP_TCP                    1
#define LWIP_UDP                    1
#define LWIP_DNS                    1
#define LWIP_TCP_KEEPALIVE          1
#define LWIP_NETIF_TX_SINGLE_PBUF   1
#define DHCP_DOES_ARP_CHECK         0
#define LWIP_DHCP_DOES_ACD_CHECK    0

#ifndef NDEBUG
#define LWIP_DEBUG                  1
#define LWIP_STATS                  1
#define LWIP_STATS_DISPLAY          1
#endif

#define ETHARP_DEBUG                LWIP_DBG_OFF
#define NETIF_DEBUG                 LWIP_DBG_OFF
#define PBUF_DEBUG                  LWIP_DBG_OFF
#define API_LIB_DEBUG               LWIP_DBG_OFF
#define API_MSG_DEBUG               LWIP_DBG_OFF
#define SOCKETS_DEBUG               LWIP_DBG_OFF
#define ICMP_DEBUG                  LWIP_DBG_OFF
#define INET_DEBUG                  LWIP_DBG_OFF
#define IP_DEBUG                    LWIP_DBG_OFF
#define IP_REASS_DEBUG              LWIP_DBG_OFF
#define RAW_DEBUG                   LWIP_DBG_OFF
#define MEM_DEBUG                   LWIP_DBG_OFF
#define MEMP_DEBUG                  LWIP_DBG_OFF
#define SYS_DEBUG                   LWIP_DBG_OFF
#define TCP_DEBUG                   LWIP_DBG_OFF
#define TCP_INPUT_DEBUG             LWIP_DBG_OFF
#define TCP_OUTPUT_DEBUG            LWIP_DBG_OFF
#define TCP_RTO_DEBUG               LWIP_DBG_OFF
#define TCP_CWND_DEBUG              LWIP_DBG_OFF
#define TCP_WND_DEBUG               LWIP_DBG_OFF
#define TCP_FR_DEBUG                LWIP_DBG_OFF
#define TCP_QLEN_DEBUG              LWIP_DBG_OFF
#define TCP_RST_DEBUG               LWIP_DBG_OFF
#define UDP_DEBUG                   LWIP_DBG_OFF
#define TCPIP_DEBUG                 LWIP_DBG_OFF
#define PPP_DEBUG                   LWIP_DBG_OFF
#define SLIP_DEBUG                  LWIP_DBG_OFF
#define DHCP_DEBUG                  LWIP_DBG_OFF

/*  in Pico-SDK above 1.5.0 */
#define MEMP_NUM_SYS_TIMEOUT            (LWIP_NUM_SYS_TIMEOUT_INTERNAL+1)

/* TCP WND must be at least 16 kb to match TLS record size
   or you will get a warning "altcp_tls: TCP_WND is smaller than the RX decrypion buffer, connection RX might stall!" */
// tls
#undef TCP_WND
#define TCP_WND  16384

#define LWIP_ALTCP               1
#define LWIP_ALTCP_TLS           1
#define LWIP_ALTCP_TLS_MBEDTLS   1

#define LWIP_DEBUG 1
#define ALTCP_MBEDTLS_DEBUG  LWIP_DBG_ON

/* set authmode */
#define ALTCP_MBEDTLS_AUTHMODE MBEDTLS_SSL_VERIFY_REQUIRED

#endif /* __LWIPOPTS_H__ */

  • mbedtls_config.h


/* Workaround for some mbedtls source files using INT_MAX without including limits.h */
#include < limits.h >

#define MBEDTLS_NO_PLATFORM_ENTROPY
#define MBEDTLS_ENTROPY_HARDWARE_ALT

#define MBEDTLS_SSL_OUT_CONTENT_LEN    2048

#define MBEDTLS_ALLOW_PRIVATE_ACCESS
#define MBEDTLS_HAVE_TIME

#define MBEDTLS_CIPHER_MODE_CBC
#define MBEDTLS_ECP_DP_SECP192R1_ENABLED
#define MBEDTLS_ECP_DP_SECP224R1_ENABLED
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
#define MBEDTLS_ECP_DP_SECP384R1_ENABLED
#define MBEDTLS_ECP_DP_SECP521R1_ENABLED
#define MBEDTLS_ECP_DP_SECP192K1_ENABLED
#define MBEDTLS_ECP_DP_SECP224K1_ENABLED
#define MBEDTLS_ECP_DP_SECP256K1_ENABLED
#define MBEDTLS_ECP_DP_BP256R1_ENABLED
#define MBEDTLS_ECP_DP_BP384R1_ENABLED
#define MBEDTLS_ECP_DP_BP512R1_ENABLED
#define MBEDTLS_ECP_DP_CURVE25519_ENABLED
#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED
#define MBEDTLS_PKCS1_V15
#define MBEDTLS_SHA256_SMALLER
#define MBEDTLS_SSL_SERVER_NAME_INDICATION
#define MBEDTLS_AES_C
#define MBEDTLS_ASN1_PARSE_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_CIPHER_C
#define MBEDTLS_CTR_DRBG_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_ERROR_C
#define MBEDTLS_MD_C
#define MBEDTLS_MD5_C
#define MBEDTLS_OID_C
#define MBEDTLS_PKCS5_C
#define MBEDTLS_PK_C
#define MBEDTLS_PK_PARSE_C
#define MBEDTLS_PLATFORM_C
#define MBEDTLS_RSA_C
#define MBEDTLS_SHA1_C
#define MBEDTLS_SHA224_C
#define MBEDTLS_SHA256_C
#define MBEDTLS_SHA512_C
#define MBEDTLS_SSL_CLI_C
#define MBEDTLS_SSL_SRV_C
#define MBEDTLS_SSL_TLS_C
#define MBEDTLS_X509_CRT_PARSE_C
#define MBEDTLS_X509_USE_C
#define MBEDTLS_AES_FEWER_TABLES

/* TLS 1.2 */
#define MBEDTLS_SSL_PROTO_TLS1_2
#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
#define MBEDTLS_GCM_C
#define MBEDTLS_ECDH_C
#define MBEDTLS_ECP_C
#define MBEDTLS_ECDSA_C
#define MBEDTLS_ASN1_WRITE_C

// The following is needed to parse a certificate
#define MBEDTLS_PEM_PARSE_C
#define MBEDTLS_BASE64_C

Rotary Encoder:

  • pio_rotary_encoder.c


#include "pico/stdio.h"
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/gpio.h"

#include "pio_rotary_encoder.h"
#include "rotary_encoder.pio.h"
#include "hardware/clocks.h"

void (*pio_callback)(uint8_t action);

void rotary_encoder_irq_handler() {
    if (pio_interrupt_get(RE_PIO, ROTARY_INT_NUM)) { // 1: clockwise, 2: countclockwise
        pio_interrupt_clear(RE_PIO, ROTARY_INT_NUM); 
        uint32_t act = pio_sm_get_blocking(RE_PIO, RE_SM_ROTARY);
        if (act == PIO_RE_CLOCKWISE || act == PIO_RE_COUNTERCLOCKWISE) { //PIO_RE_CLOCKWISE;
            pio_callback((uint8_t)act);
        } else {
            pio_callback(PIO_RE_UNKNOW);
        }
    }
    if (pio_interrupt_get(RE_PIO, SWITCH_INT_NUM)) {
        pio_interrupt_clear(RE_PIO, SWITCH_INT_NUM);
        uint8_t action  = PIO_RE_SWPRESS;

        pio_callback(action);
    }
    
    
}

void pio_rotary_switch_init(PIO pio, uint sm, uint in_base, uint freq) {
    gpio_pull_up(in_base);  // software pull-up

    pio_sm_config c;
    uint offset = pio_add_program(pio, &rotary_switch_program);

    c = rotary_switch_program_get_default_config(offset);
    
    pio_gpio_init(pio, in_base);

    pio_sm_set_consecutive_pindirs(pio, sm, in_base,  1,false);
     
    sm_config_set_in_pins(&c, in_base);
    sm_config_set_in_shift(&c, false, false, 1);

    float div = clock_get_hz(clk_sys)/freq;
    sm_config_set_clkdiv(&c, div);
    
    uint pio_irq_num = (pio==pio0) ? PIO0_IRQ_0: PIO1_IRQ_0;
    pio_set_irq0_source_enabled(pio, pis_interrupt0, true);
    pio_set_irq0_source_enabled(pio, pis_interrupt1, true);
    irq_add_shared_handler(pio_irq_num, rotary_encoder_irq_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
    irq_set_enabled(pio_irq_num, true);
    pio_sm_init(pio, sm, offset, &c);
    pio_sm_set_enabled(pio, sm, true);
}

void pio_rotary_encoder_init(PIO pio, uint sm, uint in_base, uint freq) {
    pio_sm_config c;
    uint offset = pio_add_program(pio, &rotary_encoder_program);

    c = rotary_encoder_program_get_default_config(offset);
    
    for (int i=0; i < 2; i++) pio_gpio_init(pio, in_base+i);

    pio_sm_set_consecutive_pindirs(pio, sm, in_base,  2,false);
     
    sm_config_set_in_pins(&c, in_base);
    sm_config_set_in_shift(&c, false, false, 2);

    float div = clock_get_hz(clk_sys)/freq;
    sm_config_set_clkdiv(&c, div);
    
    uint pio_irq_num = (pio==pio0) ? PIO0_IRQ_0: PIO1_IRQ_0;
    pio_set_irq0_source_enabled(pio, pis_interrupt0, true);
  
    irq_add_shared_handler(pio_irq_num, rotary_encoder_irq_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
    irq_set_enabled(pio_irq_num, true);
    pio_sm_init(pio, sm, offset, &c);
    pio_sm_set_enabled(pio, sm, true);
}



void pio_rotary_encoder_default_init() {
    pio_rotary_switch_init(RE_PIO, RE_SM_SWITCH, RE_SW_PIN, 2000);
    pio_rotary_encoder_init(RE_PIO, RE_SM_ROTARY, RE_CLK_DT_PIN, RE_SM_FREQ);
}
void pio_rotary_encoder_set_callback(void (*cb)(uint8_t action)) {
    pio_callback=cb;
}

  • pio_rotary_encoder.h


#ifndef _PIO_ROTARY_ENCODER_H_
#define _PIO_ROTARY_ENCODER_H_
#include "hardware/pio.h"

#define RE_PIO          pio0
#define RE_CLK_DT_PIN  16
#define RE_SW_PIN      18
#define RE_PIO      pio0
#define RE_SM_ROTARY      (0)
#define RE_SM_SWITCH      (1)
#define RE_SM_FREQ  100000
enum PIO_RE_ACTION{
    PIO_RE_CLOCKWISE=1,
    PIO_RE_COUNTERCLOCKWISE=2,
    PIO_RE_SWPRESS=3,
    PIO_RE_UNKNOW=4
};

void pio_rotary_switch_init(PIO pio, uint sm, uint in_base, uint freq);
void pio_rotary_encoder_init(PIO pio, uint sm, uint in_base, uint freq);
void pio_rotary_encoder_default_init();
void pio_rotary_encoder_set_callback(void (*cb)(uint8_t action));


#endif 

  • rotary_encoder.pio


.define public ROTARY_INT_NUM 0
.define public SWITCH_INT_NUM 1
.program rotary_encoder
start:
in pins, 2 [31]
mov x, isr          ; CLK, DT present state
mov y, osr          ; CLK, DT previous state
jmp x!=y, rotary    ; not eaqual, rotary occure
jmp saveoldvalue    ; clear ISR
rotary:
set y, 0b01         ;
jmp x!=y, pos10
jmp check_dir
pos10:
set y, 0b10
jmp x!=y, saveoldvalue
jmp check_dir
saveoldvalue:
mov osr, isr 
in NULL, 32  ;; imporant to clear ISR
jmp start 

check_dir:
out y,1     ; previous state DT bit
mov osr,x   ; save oldvalue
in NULL, 32 ; clear ISR
in x, 1     ; shift in present state DT bit 
mov x, isr  ; present state DT bit
jmp x!=y counterclockwise
jmp clockwise

clockwise:
set y,1         ; clockwise: value = 1
jmp output
counterclockwise:
set y,2         ; counterclockwise: value = 2
output:
mov isr, y
push
irq wait ROTARY_INT_NUM
jmp start  

.program rotary_switch
wait 0 pin 0 [31]       ; Switch PIN is initially pulled up 
irq wait SWITCH_INT_NUM              ; [31] wait for button bounce
wait 1 pin 0 [31]



  • CMakeLists.txt


add_library(rotary_encoder INTERFACE)
pico_generate_pio_header(rotary_encoder ${CMAKE_CURRENT_LIST_DIR}/rotary_encoder.pio)
target_sources(rotary_encoder INTERFACE
    ${CMAKE_CURRENT_LIST_DIR}/pio_rotary_encoder.c
)

target_include_directories(rotary_encoder INTERFACE
    ${CMAKE_CURRENT_LIST_DIR}

)

target_link_libraries(rotary_encoder INTERFACE
        hardware_pio
)

ws2812:

  • ws2812.c


#include "pico/stdio.h"
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "ws2812.pio.h"
#include "ws2812.h"

void ws2812_pio_init(PIO pio, uint sm, uint pin, float freq) {
    pio_gpio_init(pio, pin);
    pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);

    uint offset = pio_add_program(pio, &ws2812_program);

    pio_sm_config c = ws2812_program_get_default_config(offset);
    sm_config_set_sideset_pins(&c, pin);
    sm_config_set_out_shift(&c, false, true, 24);
    sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);

    int cycles_per_bit = 6;
    float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
    sm_config_set_clkdiv(&c, div);

    pio_sm_init(pio, sm, offset, &c);
    pio_sm_set_enabled(pio, sm, true);
}

void ws2812_put_pixel(uint32_t pixel_grb) {
    pio_sm_put_blocking(WS2812_PIO, WS2812_SM, pixel_grb << 8u);
}
void ws2812_put_pixel_rgb(uint32_t pixel_rgb) {
    uint32_t grb = ((pixel_rgb & 0xff00) >> 8) << 16 | ((pixel_rgb & 0xff0000) >> 16) << 8 | pixel_rgb &0xff; 
    ws2812_put_pixel(grb);
}
void ws2812_default_pio_init() {
    ws2812_pio_init(WS2812_PIO, WS2812_SM, WS2812_PIN, WS2812_PIO_FREQ);
}

  • ws2812.h


#ifndef _WS2812_H_
#define _WS2812_H_

#include "hardware/pio.h"

#define WS2812_PIO pio1
#define WS2812_SM   (1)
#define WS2812_PIN  (19)
#define WS2812_PIO_FREQ (800000)

void ws2812_pio_init(PIO pio, uint sm, uint pin, float freq);
void ws2812_default_pio_init();
void ws2812_put_pixel(uint32_t pixel_grb);
void ws2812_put_pixel_rgb(uint32_t pixel_rgb);
#endif

  • ws2812.pio


.program ws2812
.side_set 1

.wrap_target
bitloop:
    out x, 1       side 0 [1]
    jmp !x do_zero side 1 [1]
do_one:
    jmp  bitloop   side 1 [1]
do_zero:
    nop            side 0 [1]
.wrap

  • CMakeLists.txt


add_library(ws2812 INTERFACE)
pico_generate_pio_header(ws2812 ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio)
target_sources(ws2812 INTERFACE
    ${CMAKE_CURRENT_LIST_DIR}/ws2812.c
)

target_include_directories(ws2812 INTERFACE
    ${CMAKE_CURRENT_LIST_DIR}

)

target_link_libraries(ws2812 INTERFACE
        hardware_pio
)


沒有留言:

張貼留言