If I was allowed to post links or upload I would share the project since it is at an early stage but here is the requested code. Something of note is I am using Jon Durrant’s code example for task classes. He calls them an agentClass. Specifically, from his example RPIPicoFreeRTOSSMPExp on github.
main.cpp
/***
- Demo program to use FreeRTOS SMP on Both Cores
- Blink on Core 0 to GPIO 0
- Blink on Core 1 to GPIO 15
- Counter showing on GPIO 2 to 5, using Core 1
- Instructions sent to Counter from Main Task on Core 0
- Jon Durrant
- 15-Aug-2022
*/
// — UART — From pico_w.h
// This should move the printf function to use UART1-6,7
//#define PICO_DEFAULT_UART 1
//#define PICO_DEFAULT_UART_TX_PIN 6
//#define PICO_DEFAULT_UART_RX_PIN 7
#include “pico/stdlib.h”
#include “FreeRTOS.h”
#include “task.h”
#include “semphr.h”
#include <stdio.h>
#include <math.h>
#include “hardware/structs/sio.h”
#include “pico/cyw43_arch.h”
#include “macro.h”
#include “BlinkTask.h”
#include “PIO/PIOTask.h”
#include “UI/UITask.h”
#include “Control/ControlTask.h”
#include “hardware/i2c.h”
#include “PowerLedClass.h”
#include “queue.h”
#include “global.h”
RESOURCES resource_mutex; // Global list of Mutex locks for the board resoureces.
//Standard Task priority
#define TASK_PRIORITY ( tskIDLE_PRIORITY + 1UL )
//LED PAD to use
#define VERSION_MAJOR 0
#define VERSION_MINOR 4
#define PH_RESET_BUTTON 20
//#define GPIO_EVENT_HIGH 0x01
//#define GPIO_EVENT_LOW 0x02
//#define GPIO_EVENT_FALLING 0x04
//#define GPIO_EVENT_RISING 0x08
#define DEBOUNCE_TIME 100
#define LONG_PRESS_TIME 10000
QueueHandle_t Button_Queue;
/***
- reserved_addr - Checks for an i2c reserved address
-
@param params - address (7bit)
- only used by i2c_scan
*/
bool reserved_addr(uint8_t addr) {
// I2C reserves some addresses for special purposes. We exclude these from the scan.
// These are any addresses of the form 000 0xxx or 111 1xxx
return (addr & 0x78) == 0 || (addr & 0x78) == 0x78;
}
/***
-
i2c_scan - Scans the bus to find valid addresses
-
@param params - I2c bus
/
void i2c_scan (i2c_inst_t I2C_channel,unsigned char ch) {
printf(“\nI2C%d Bus Scan\n”, ch);
printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
for (int addr = 0; addr < (1 << 7); ++addr) {
if (addr % 16 == 0) {
printf("%02x ", addr);
}
// Perform a 1-byte dummy read from the probe address. If a slave
// acknowledges this address, the function returns the number of bytes
// transferred. If the address byte is ignored, the function returns
// -1.
// Skip over any reserved addresses.
int ret;
uint8_t rxdata;
if (reserved_addr(addr))
ret = PICO_ERROR_GENERIC;
else
r_display(ret = i2c_read_blocking(I2C_channel, addr, &rxdata, 1, false));
printf(ret < 0 ? "." : "@");
printf(addr % 16 == 15 ? "\n" : " ");
}
printf(“Done.\n”);
}
void gpio_callback(uint gpio, uint32_t events) {
static TickType_t phTime;
unsigned char message;
//BaseType_t xHigherPriorityTaskWoken = pdFALSE;
switch (gpio){
case PH_RESET_BUTTON:
//printf("INT: gpio=%d events=%d ", gpio, events);
if (events == GPIO_IRQ_EDGE_FALL){
//printf("fall. ticks=%d\n", phTime);
if (phTime == 0){
phTime = xTaskGetTickCount();
// Queue PH button down message with phTime.
}
} else if (events == GPIO_IRQ_EDGE_RISE) {
//printf("rise. ticks=%d\n", xTaskGetTickCount() - phTime);
if (phTime != 0) {
if (xTaskGetTickCount() - phTime > LONG_PRESS_TIME) {
// Queue button Long push message.
message = BUTTON_MESSAGE_PH_FACTORY_RESET;
//xQueueSendFromISR (Button_Queue, &message, NULL);
}
if (xTaskGetTickCount() - phTime > DEBOUNCE_TIME) {
// Queue button push message
message = BUTTON_MESSAGE_PH_PRESSED;
//xQueueSendFromISR (Button_Queue, &message, NULL);
}
}
phTime = 0;
}
break;
}
}
/***
-
Main task to blink external LED
-
@param params - unused
*/
void mainTask(void *params){
r_uart(printf(“Main task started\n”));
// Configure the chip
// I2C0 BUS for PORTS, RTC, FLASH
uint baud;
gpio_set_function(16, GPIO_FUNC_I2C); // Set pins 12 and 13 for i2c role
gpio_set_function(17, GPIO_FUNC_I2C);
baud = i2c_init(i2c0, 100000); // Init i2c0 and set baud rate to 100000hz (1Mhz max supported speed by pico)
r_uart(printf(“I2C0: i2c_init actual baudrate set to %d.\n”, baud));
r_uart(i2c_scan(i2c0, 0));
// I2C1 BUS for DISPLAY
gpio_set_function(18, GPIO_FUNC_I2C); // Set pins 12 and 13 for i2c role
gpio_set_function(19, GPIO_FUNC_I2C);
baud = i2c_init(i2c1, 100000); // Init i2c0 and set baud rate to 1Mhz (max supported speed by pico)
r_uart(printf(“I2C1: i2c_init actual baudrate set to %d.\n”, baud));
r_uart(i2c_scan(i2c1, 1));
// Power LED
gpio_init(POWERLED_GPIO);
gpio_set_function(POWERLED_GPIO, GPIO_FUNC_PWM);
gpio_set_dir(POWERLED_GPIO, true); // Power LED GPIO21 is set to output.
// PH / Reset Button
gpio_init(PH_RESET_BUTTON);
gpio_set_dir(PH_RESET_BUTTON, false); // is set to input.
gpio_pull_up(PH_RESET_BUTTON);
gpio_set_irq_enabled_with_callback(PH_RESET_BUTTON, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
Button_Queue = xQueueCreate( 10, sizeof( unsigned char) );
if( Button_Queue == NULL )
{
/* Queue was not created and must not be used. */
r_uart(printf(“Button_Queue: failed to create queue!\n”));
}
int num = uxQueueMessagesWaiting( Button_Queue );
r_uart(printf(“Button_Queue: Number of messages = %d.\n”, num));
// CREATE TASKS
BlinkTask blink(LED_PAD);
UITask ui;
//PIOTask pio;
ControllTask control;
blink.start(“Blink 0”, TASK_PRIORITY); num = uxQueueMessagesWaiting( Button_Queue );
ui.start(“UI”, TASK_PRIORITY);
// pio.start(“PIO”, TASK_PRIORITY);
control.start(“Control”, TASK_PRIORITY);
//Bind to CORE 1
UBaseType_t coreMask = 0x2;
vTaskCoreAffinitySet( ui.getTask(), coreMask );
//Bind to CORE 0
coreMask = 0x1;
vTaskCoreAffinitySet( blink.getTask(), coreMask );
vTaskCoreAffinitySet( control.getTask(), coreMask );
while (true) { // Loop forever
r_uart(printf(“Main ping on Core %ld\n”,sio_hw->cpuid));
int num = uxQueueMessagesWaiting( Button_Queue );
r_uart(printf(“Button_Queue: Number of messages = %d.\n”, num));
vTaskDelay(60000);
}
}
/***
-
Launch the tasks and scheduler
*/
void vLaunch( void) {
//Start blink task
TaskHandle_t task;
xTaskCreate(mainTask, “MainThread”, 512, NULL, TASK_PRIORITY, &task);
/* Start the tasks and timer running. */
vTaskStartScheduler();
}
/***
-
Main
-
@return
*/
int main( void )
{
//Setup serial over USB and give a few seconds to settle before we start
//stdio_uart_init_full (uart1, 115200, 6, 7);
stdio_init_all();
cyw43_arch_init();
sleep_ms(2000);
printf(“\n\n\n\nGO…\n”);
printf(“Hydrocontroler V%d.%d\n”, VERSION_MAJOR, VERSION_MINOR);
printf(“Copyright DienstNet LLC 2022\n”);
printf(“All Rights Reserved.\n\n”);
//Hardware Mutexes
resource_mutex.wifi_resource = xSemaphoreCreateMutex();
resource_mutex.display_resource = xSemaphoreCreateMutex();
resource_mutex.i2c_resource = xSemaphoreCreateMutex();
resource_mutex.uart_resource = xSemaphoreCreateMutex();
//Start tasks and scheduler
const char *rtos_name;
#if ( portSUPPORT_SMP == 1 )
rtos_name = “FreeRTOS SMP”;
#else
rtos_name = “FreeRTOS”;
#endif
#if ( portSUPPORT_SMP == 1 ) && ( configNUM_CORES == 2 )
printf(“%s on both cores:\n”, rtos_name);
#else
printf(“Starting %s on core 0:\n”, rtos_name);
#endif
vLaunch();
return 0;
}
ControlTask.h
/*
- ControllTask.h
-
- Hydroponic system controll application task.
-
- Created on: 11/30/22
-
Author: dddienst
*/
#ifndef ControllTask_H_
#define ControllTask_H_
#include “pico/stdlib.h”
#include “FreeRTOS.h”
#include “taskClass.h”
#include “Control/ControlTask.h”
class ControllTask: public Task {
public:
/***
* Constructor
*/
ControllTask();
/***
* Destructor
*/
virtual ~ControllTask();
protected:
/***
* Run loop for the Task.
*/
virtual void run();
/***
* Get the static depth required in words
* @return - words
*/
virtual configSTACK_DEPTH_TYPE getMaxStackSize();
//Class Variables
};
#endif /* ControllTask_H_ */
ControlTask.cpp
/*
- ControllTask.cpp
-
- Created on: 11/30/22
-
Author: dddienst
- Runs only on core 0
*/
#include “Control/ControlTask.h”
#include “Control/Relays.h”
#include “stdio.h”
#include “macro.h”
#include “pico/multicore.h”
#include “global.h”
#include “queue.h”
UBaseType_t _uxQueueMessagesWaiting(QueueHandle_t xQueue) {
int num = uxQueueMessagesWaiting(xQueue);
return uxQueueMessagesWaiting(xQueue);
}
BaseType_t _xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait) {
return xQueueReceive(xQueue, pvBuffer, xTicksToWait);
}
/***
- Constructor
-
@param none
*/
ControllTask::ControllTask() {
}
/***
- Destructor
*/
ControllTask::~ControllTask() {
stop();
}
/***
-
Main Run Task for Task
*/
void ControllTask::run(){
r_uart(printf(“%s started on Core %d\n”, pName, getCore()));
unsigned char message = 0;
unsigned int loop;
UBaseType_t num;
num = uxQueueMessagesWaiting( Button_Queue ); r_uart(printf(“Button_Queue Control start1: Number of messages = %d.\n”, num));
//RELAYS relay(i2c0);
// relay.test();
num = uxQueueMessagesWaiting( Button_Queue ); r_uart(printf(“Button_Queue Control start2: Number of messages = %d.\n”, num));
while (true) { // Loop forever
if (num = uxQueueMessagesWaiting( Button_Queue ) > 0 ) {
if (xQueueReceive(Button_Queue, &message, 0) == pdTRUE){
r_uart(printf("Messages in Q=%d message=%d\n", num, message));
switch (message) {
case BUTTON_MESSAGE_PH_PRESSED:
r_uart(printf("Take a PH reading.\n"));
break;
case BUTTON_MESSAGE_PH_FACTORY_RESET:
r_uart(printf("Perform a Factory Reset.\n"));
break;
}
}
}
vTaskDelay(1000);
loop++;
if (loop == 30) {
r_uart(printf("%s ping on Core %d\n", pName, getCore()));
loop = 0;
}
}
}
/***
- Get the static depth required in words
-
@return - words
*/
configSTACK_DEPTH_TYPE ControllTask::getMaxStackSize(){
return 1024;
}