229 lines
6.7 KiB
C
229 lines
6.7 KiB
C
/**
|
|
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
//
|
|
// Created by elliot on 25/05/24.
|
|
//
|
|
#include "pico/stdlib.h"
|
|
#include "pico/cyw43_arch.h"
|
|
#include "pico/unique_id.h"
|
|
#include "hardware/gpio.h"
|
|
#include "hardware/irq.h"
|
|
#include "hardware/adc.h"
|
|
#include "lwip/apps/mqtt.h"
|
|
#include "lwip/apps/mqtt_priv.h" // needed to set hostname
|
|
#include "lwip/dns.h"
|
|
#include "lwip/altcp_tls.h"
|
|
|
|
// Temperature
|
|
#ifndef TEMPERATURE_UNITS
|
|
#define TEMPERATURE_UNITS 'C' // Set to 'F' for Fahrenheit
|
|
#endif
|
|
|
|
#ifndef MQTT_SERVER
|
|
#error Need to define MQTT_SERVER
|
|
#endif
|
|
|
|
// This file includes your client certificate for client server authentication
|
|
#ifdef MQTT_CERT_INC
|
|
#include MQTT_CERT_INC
|
|
#endif
|
|
|
|
#ifndef MQTT_TOPIC_LEN
|
|
#define MQTT_TOPIC_LEN 100
|
|
#endif
|
|
|
|
typedef struct {
|
|
mqtt_client_t* mqtt_client_inst;
|
|
struct mqtt_connect_client_info_t mqtt_client_info;
|
|
char data[MQTT_OUTPUT_RINGBUF_SIZE];
|
|
char topic[MQTT_TOPIC_LEN];
|
|
uint32_t len;
|
|
ip_addr_t mqtt_server_address;
|
|
bool connect_done;
|
|
int subscribe_count;
|
|
bool stop_client;
|
|
} MQTT_CLIENT_DATA_T;
|
|
|
|
#ifndef DEBUG_printf
|
|
#ifndef NDEBUG
|
|
#define DEBUG_printf printf
|
|
#else
|
|
#define DEBUG_printf(...)
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef INFO_printf
|
|
#define INFO_printf printf
|
|
#endif
|
|
|
|
#ifndef ERROR_printf
|
|
#define ERROR_printf printf
|
|
#endif
|
|
|
|
// how often to measure our temperature
|
|
#define TEMP_WORKER_TIME_S 10
|
|
|
|
// keep alive in seconds
|
|
#define MQTT_KEEP_ALIVE_S 60
|
|
|
|
// qos passed to mqtt_subscribe
|
|
// At most once (QoS 0)
|
|
// At least once (QoS 1)
|
|
// Exactly once (QoS 2)
|
|
#define MQTT_PUBLISH_QOS 1
|
|
#define MQTT_PUBLISH_RETAIN 0
|
|
#define MQTT_DEVICE_NAME "pico_test_001"
|
|
|
|
// Set to 1 to add the client name to topics, to support multiple devices using the same server
|
|
//
|
|
#define MQTT_TOPIC "bws/test1/state"
|
|
|
|
/* References for this implementation:
|
|
* raspberry-pi-pico-c-sdk.pdf, Section '4.1.1. hardware_adc'
|
|
* pico-examples/adc/adc_console/adc_console.c */
|
|
static float read_onboard_temperature(const char unit) {
|
|
|
|
/* 12-bit conversion, assume max value == ADC_VREF == 3.3 V */
|
|
const float conversionFactor = 3.3f / (1 << 12);
|
|
|
|
float adc = (float)adc_read() * conversionFactor;
|
|
float tempC = 27.0f - (adc - 0.706f) / 0.001721f;
|
|
|
|
if (unit == 'C' || unit != 'F') {
|
|
return tempC;
|
|
} else if (unit == 'F') {
|
|
return tempC * 9 / 5 + 32;
|
|
}
|
|
|
|
return -1.0f;
|
|
}
|
|
|
|
static void pub_request_cb(__unused void *arg, err_t err) {
|
|
if (err != 0) {
|
|
ERROR_printf("pub_request_cb failed %d", err);
|
|
}
|
|
}
|
|
|
|
static void publish_temperature(MQTT_CLIENT_DATA_T *state) {
|
|
static float old_temperature;
|
|
float temperature = read_onboard_temperature(TEMPERATURE_UNITS);
|
|
if (temperature != old_temperature) {
|
|
old_temperature = temperature;
|
|
// Publish temperature on /temperature topic
|
|
char temp_str[16];
|
|
snprintf(temp_str, sizeof(temp_str), "%.2f", temperature);
|
|
INFO_printf("Publishing Temp %s\n", temp_str);
|
|
mqtt_publish(state->mqtt_client_inst, MQTT_TOPIC, temp_str, strlen(temp_str), MQTT_PUBLISH_QOS, MQTT_PUBLISH_RETAIN, pub_request_cb, state);
|
|
}
|
|
}
|
|
|
|
static void sub_request_cb(void *arg, err_t err) {
|
|
MQTT_CLIENT_DATA_T* state = (MQTT_CLIENT_DATA_T*)arg;
|
|
if (err != 0) {
|
|
panic("subscribe request failed %d", err);
|
|
}
|
|
state->subscribe_count++;
|
|
}
|
|
|
|
static void unsub_request_cb(void *arg, err_t err) {
|
|
MQTT_CLIENT_DATA_T* state = (MQTT_CLIENT_DATA_T*)arg;
|
|
if (err != 0) {
|
|
panic("unsubscribe request failed %d", err);
|
|
}
|
|
state->subscribe_count--;
|
|
assert(state->subscribe_count >= 0);
|
|
|
|
// Stop if requested
|
|
if (state->subscribe_count <= 0 && state->stop_client) {
|
|
mqtt_disconnect(state->mqtt_client_inst);
|
|
}
|
|
}
|
|
|
|
static void temperature_worker_fn(async_context_t *context, async_at_time_worker_t *worker) {
|
|
MQTT_CLIENT_DATA_T* state = (MQTT_CLIENT_DATA_T*)worker->user_data;
|
|
publish_temperature(state);
|
|
async_context_add_at_time_worker_in_ms(context, worker, TEMP_WORKER_TIME_S * 1000);
|
|
}
|
|
static async_at_time_worker_t temperature_worker = { .do_work = temperature_worker_fn };
|
|
|
|
static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status) {
|
|
MQTT_CLIENT_DATA_T* state = (MQTT_CLIENT_DATA_T*)arg;
|
|
if (status == MQTT_CONNECT_ACCEPTED) {
|
|
state->connect_done = true;
|
|
|
|
// Publish temperature every 10 sec if it's changed
|
|
temperature_worker.user_data = state;
|
|
async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &temperature_worker, 0);
|
|
} else if (status == MQTT_CONNECT_DISCONNECTED) {
|
|
if (!state->connect_done) {
|
|
panic("Failed to connect to mqtt server");
|
|
}
|
|
}
|
|
else {
|
|
panic("Unexpected status");
|
|
}
|
|
}
|
|
|
|
static void start_client(MQTT_CLIENT_DATA_T *state) {
|
|
const int port = MQTT_PORT;
|
|
state->mqtt_client_inst = mqtt_client_new();
|
|
if (!state->mqtt_client_inst) {
|
|
panic("MQTT client instance creation error");
|
|
}
|
|
INFO_printf("IP address of this device %s\n", ipaddr_ntoa(&(netif_list->ip_addr)));
|
|
INFO_printf("Connecting to mqtt server at %s\n", ipaddr_ntoa(&state->mqtt_server_address));
|
|
|
|
cyw43_arch_lwip_begin();
|
|
if (mqtt_client_connect(state->mqtt_client_inst, &state->mqtt_server_address, port, mqtt_connection_cb, state, &state->mqtt_client_info) != ERR_OK) {
|
|
panic("MQTT broker connection error");
|
|
}
|
|
cyw43_arch_lwip_end();
|
|
}
|
|
|
|
int main(void) {
|
|
stdio_init_all();
|
|
INFO_printf("mqtt client starting\n");
|
|
|
|
adc_init();
|
|
adc_set_temp_sensor_enabled(true);
|
|
adc_select_input(4);
|
|
|
|
static MQTT_CLIENT_DATA_T state;
|
|
|
|
if (cyw43_arch_init()) {
|
|
panic("Failed to inizialize CYW43");
|
|
}
|
|
|
|
state.mqtt_client_info.client_id = MQTT_DEVICE_NAME;
|
|
state.mqtt_client_info.keep_alive = MQTT_KEEP_ALIVE_S; // Keep alive in sec
|
|
state.mqtt_client_info.client_user = MQTT_USERNAME;
|
|
state.mqtt_client_info.client_pass = MQTT_PASSWORD;
|
|
ip_addr_t ip_addr;
|
|
// Initialize the IP address
|
|
if (ipaddr_aton(MQTT_SERVER, &ip_addr)) {
|
|
printf("IP address is valid and initialized.\n");
|
|
} else {
|
|
printf("Invalid IP address format.\n");
|
|
}
|
|
state.mqtt_server_address = ip_addr;
|
|
|
|
cyw43_arch_enable_sta_mode();
|
|
if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) {
|
|
panic("Failed to connect");
|
|
}
|
|
INFO_printf("\nConnected to Wifi\n");
|
|
|
|
start_client(&state);
|
|
|
|
while (!state.connect_done || mqtt_client_is_connected(state.mqtt_client_inst)) {
|
|
cyw43_arch_poll();
|
|
cyw43_arch_wait_for_work_until(make_timeout_time_ms(10000));
|
|
}
|
|
|
|
INFO_printf("mqtt client exiting\n");
|
|
return 0;
|
|
}
|