aboutsummaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
Diffstat (limited to 'main')
-rw-r--r--main/.gitignore2
-rw-r--r--main/CMakeLists.txt33
-rw-r--r--main/FreeRTOSConfig.h69
-rw-r--r--main/config.def.h11
-rw-r--r--main/i2c.c93
-rw-r--r--main/i2c.h44
-rw-r--r--main/init.c59
-rw-r--r--main/init.h38
l---------main/lib1
m---------main/lib/pico-sdk0
-rw-r--r--main/lwipopts.h23
-rw-r--r--main/main.c32
-rw-r--r--main/main.cpp36
-rw-r--r--main/makefile9
-rw-r--r--main/readme.md21
-rw-r--r--main/sock.c96
-rw-r--r--main/sock.h11
17 files changed, 523 insertions, 55 deletions
diff --git a/main/.gitignore b/main/.gitignore
index 7c3ba25..0e56cf2 100644
--- a/main/.gitignore
+++ b/main/.gitignore
@@ -1,3 +1 @@
config.h
-build
-.cache
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index e24d9a5..6390d7c 100644
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -1,22 +1,39 @@
cmake_minimum_required(VERSION 3.29)
-include(lib/pico-sdk/pico_sdk_init.cmake)
-
-project(puzzlebox_main C CXX ASM)
-
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
set(PICO_BOARD pico_w)
+include(lib/pico-sdk/pico_sdk_init.cmake)
+include(lib/FreeRTOS-Kernel/portable/ThirdParty/GCC/RP2040/FreeRTOS_Kernel_import.cmake)
+include(../i2ctcp/include.cmake)
+
+project(puzzlebox_main C CXX ASM)
+
pico_sdk_init()
add_executable(main
- main.cpp
-)
+ main.c
+ init.c
+ sock.c
+ i2c.c
+ )
pico_enable_stdio_usb(main 1)
-# pico_enable_stdio_uart(main 1)
+pico_enable_stdio_uart(main 0)
pico_add_extra_outputs(main)
+
+include_directories(lib/pico-sdk/lib/lwip/contrib/ports/freertos/include)
+
target_include_directories(main PRIVATE ${CMAKE_CURRENT_LIST_DIR})
-target_link_libraries(main pico_cyw43_arch_lwip_threadsafe_background pico_stdlib)
+target_link_libraries(main
+ pico_cyw43_arch_lwip_sys_freertos
+ pico_stdlib
+ hardware_i2c
+ FreeRTOS-Kernel
+ FreeRTOS-Kernel-Heap4
+ i2ctcp
+ mpack
+ )
+
diff --git a/main/FreeRTOSConfig.h b/main/FreeRTOSConfig.h
new file mode 100644
index 0000000..c811296
--- /dev/null
+++ b/main/FreeRTOSConfig.h
@@ -0,0 +1,69 @@
+#pragma once
+// values from pico-examples/pico_w/wifi/freertos
+
+#define configUSE_PREEMPTION 1
+#define configUSE_TICKLESS_IDLE 0
+#define configUSE_IDLE_HOOK 0
+#define configUSE_TICK_HOOK 0
+#define configTICK_RATE_HZ ((TickType_t) 1000)
+#define configMAX_PRIORITIES 32
+#define configMINIMAL_STACK_SIZE ((configSTACK_DEPTH_TYPE) 512)
+#define configUSE_16_BIT_TICKS 0
+#define configIDLE_SHOULD_YIELD 1
+#define configUSE_MUTEXES 1
+#define configUSE_RECURSIVE_MUTEXES 1
+#define configUSE_APPLICATION_TASK_TAG 0
+#define configUSE_COUNTING_SEMAPHORES 1
+#define configQUEUE_REGISTRY_SIZE 8
+#define configUSE_QUEUE_SETS 1
+#define configUSE_TIME_SLICING 1
+#define configUSE_NEWLIB_REENTRANT 0
+#define configENABLE_BACKWARD_COMPATIBILITY 1
+#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5
+#define configSTACK_DEPTH_TYPE uint32_t
+#define configMESSAGE_BUFFER_LENGTH_TYPE size_t
+#define configSUPPORT_STATIC_ALLOCATION 0
+#define configSUPPORT_DYNAMIC_ALLOCATION 1
+#define configTOTAL_HEAP_SIZE (128 * 1024)
+#define configAPPLICATION_ALLOCATED_HEAP 0
+#define configCHECK_FOR_STACK_OVERFLOW 0
+#define configUSE_MALLOC_FAILED_HOOK 0
+#define configUSE_DAEMON_TASK_STARTUP_HOOK 0
+#define configGENERATE_RUN_TIME_STATS 0
+#define configUSE_TRACE_FACILITY 1
+#define configUSE_STATS_FORMATTING_FUNCTIONS 0
+#define configUSE_CO_ROUTINES 0
+#define configMAX_CO_ROUTINE_PRIORITIES 1
+#define configUSE_TIMERS 1
+#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1)
+#define configTIMER_QUEUE_LENGTH 10
+#define configTIMER_TASK_STACK_DEPTH 1024
+
+// #define configNUM_CORES 2
+// #define configTICK_CORE 0
+// #define configRUN_MULTIPLE_PRIORITIES 1
+// #define configUSE_CORE_AFFINITY 1
+
+#define configSUPPORT_PICO_SYNC_INTEROP 1
+#define configSUPPORT_PICO_TIME_INTEROP 1
+
+#include <assert.h>
+#define configASSERT(x) assert(x)
+
+#define INCLUDE_vTaskPrioritySet 1
+#define INCLUDE_uxTaskPriorityGet 1
+#define INCLUDE_vTaskDelete 1
+#define INCLUDE_vTaskSuspend 1
+#define INCLUDE_vTaskDelayUntil 1
+#define INCLUDE_vTaskDelay 1
+#define INCLUDE_xTaskGetSchedulerState 1
+#define INCLUDE_xTaskGetCurrentTaskHandle 1
+#define INCLUDE_uxTaskGetStackHighWaterMark 1
+#define INCLUDE_xTaskGetIdleTaskHandle 1
+#define INCLUDE_eTaskGetState 1
+#define INCLUDE_xTimerPendFunctionCall 1
+#define INCLUDE_xTaskAbortDelay 1
+#define INCLUDE_xTaskGetHandle 1
+#define INCLUDE_xTaskResumeFromISR 1
+#define INCLUDE_xQueueGetMutexHolder 1
+
diff --git a/main/config.def.h b/main/config.def.h
index 48de559..7fcaed9 100644
--- a/main/config.def.h
+++ b/main/config.def.h
@@ -1,8 +1,17 @@
#pragma once
+#include <pico/cyw43_arch.h>
+// wifi credentials
#define CONF_NET_SSID "network name"
#define CONF_NET_PASS "network password"
+#define CONF_NET_AUTH CYW43_AUTH_WPA2_AES_PSK
+// max duration (milliseconds) for establishing wifi connection
+#define CONF_NET_CONN_TIMEOUT 10e3
-#include "cyw43_country.h"
+#include <cyw43_country.h>
#define CONF_NET_COUNTRY CYW43_COUNTRY_NETHERLANDS
+#define CONF_SRV_PORT 9191
+
+#define LED_PIN CYW43_WL_GPIO_LED_PIN
+
diff --git a/main/i2c.c b/main/i2c.c
new file mode 100644
index 0000000..b324124
--- /dev/null
+++ b/main/i2c.c
@@ -0,0 +1,93 @@
+#include "i2c.h"
+#include "init.h"
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <pico/stdlib.h>
+#include <hardware/i2c.h>
+
+void init_i2c() {
+ i2c_init(I2C_PORT, 100 * 1000); // currently at 100kHz
+
+ // Initialize I2C pins - sda(16), scl(17)
+ gpio_set_function(SDA_PIN, GPIO_FUNC_I2C);
+ gpio_set_function(SCL_PIN, GPIO_FUNC_I2C);
+
+ gpio_pull_up(SDA_PIN);
+ gpio_pull_up(SCL_PIN);
+}
+
+int read_i2c(uint8_t addr, uint8_t *output, size_t len) {
+ // false - finished with bus
+ return i2c_read_blocking (I2C_PORT, addr, output, len, false);
+}
+
+int write_i2c(uint8_t addr, uint8_t *input, size_t len) {
+ // true to keep master control of bus
+ return i2c_write_blocking (I2C_PORT, addr, input, len, true);
+}
+
+bool reserved_addr(uint8_t addr) {
+ return (addr & 0x78) == 0 || (addr & 0x78) == 0x78;
+}
+
+void init_addr_array(uint8_t array[], int size) {
+ for(int i = 0; i < size; i++){
+ array[i] = 0x00;
+ }
+}
+
+uint8_t* scan_bus(uint8_t *array) {
+ int ret;
+ int i = 0;
+ uint8_t rxdata;
+
+ for(int addr = 0; addr < (1<<7); addr++) {
+ // ignore reserved addresses
+ // These are any addresses of the form 000 0xxx or 111 1xxx
+ if( reserved_addr(addr) ){
+ ret = PICO_ERROR_GENERIC;
+ }else{
+ ret = i2c_read_blocking(I2C_PORT, addr, &rxdata, 1, false);
+ }
+
+ // if acknowledged -> ret == number of bytes sent
+ if(ret > 0){
+ printf("found i2c slave on addr: %d\n", addr);
+ array[i] = addr;
+ i++;
+ }
+ }
+
+ return array;
+}
+
+void bus_task() {
+ // scan bus for slaves
+ // send updates at regular intervals
+ await_init();
+
+ int i = 0;
+ uint8_t found[MAX_SLAVES];
+ init_addr_array(found, MAX_SLAVES);
+
+ while(1) {
+ // printf("Bus scan!");
+ scan_bus(found);
+
+ for(int i = 0; i < MAX_SLAVES; i++){
+ if( found[i] == 0x00 )
+ break;
+
+ uint8_t data = 0x01;
+ // send data to found slave address
+ write_i2c(found[i], &data, 1);
+
+ data = 0x02;
+ write_i2c(found[i], &data, 1);
+ // request update from slave addr at found[i]
+ //write_i2c();
+ }
+ }
+}
diff --git a/main/i2c.h b/main/i2c.h
new file mode 100644
index 0000000..5ad5cfb
--- /dev/null
+++ b/main/i2c.h
@@ -0,0 +1,44 @@
+#pragma once
+// https://github.com/raspberrypi/pico-examples/tree/master/i2c
+
+#include <stddef.h>
+#include <stdint.h>
+#include <hardware/i2c.h>
+
+#define SDA_PIN 16
+#define SCL_PIN 17
+#define I2C_PORT i2c0
+#define MAX_SLAVES 10
+
+/**
+ * \brief initialize all required gpio for i2c usage on the pico
+ *
+ * This functions only initializes the standard gpio required to start i2c
+ * communications.
+ *
+ * \note Tasks shouldn't depend on any other module in the main controller
+ */
+void init_i2c();
+
+/**
+ * \brief read data from addr with length len from i2c bus.
+ *
+ * This functions reads data from a specific address on the i2c bus,
+ * the output var will hold the data which was read from said address with
+ * length len.
+ */
+int read_i2c(uint8_t addr, uint8_t *output, size_t len);
+
+/**
+ * \brief write data to addr with length len from i2c bus.
+ * \param addr
+ * \param input
+ * \param len
+ * This functions writes data to a specific address on the i2c bus,
+ * the input var holds the data which will be written to the given
+ * address with length len.
+ */
+int write_i2c(uint8_t addr, uint8_t *input, size_t len);
+
+/** \brief looking for slave addresses and requesting updates */
+void bus_task();
diff --git a/main/init.c b/main/init.c
new file mode 100644
index 0000000..08177c7
--- /dev/null
+++ b/main/init.c
@@ -0,0 +1,59 @@
+#include "config.h"
+#include "init.h"
+#include "i2c.h"
+
+#include <FreeRTOS.h>
+#include <task.h>
+#include <event_groups.h>
+
+#include <pico/stdio.h>
+#include <pico/cyw43_arch.h>
+
+EventGroupHandle_t init_complete;
+
+static void init_stdio() {
+ stdio_init_all();
+}
+
+static void init_cyw34() {
+ if (cyw43_arch_init_with_country(CONF_NET_COUNTRY))
+ panic("cyw43_arch_init_with_country failed\n");
+}
+
+static void init_wifi() {
+ // enable 'station' mode (connect to an access point instead of acting like one)
+ cyw43_arch_enable_sta_mode();
+
+ // if (cyw43_arch_wifi_connect_timeout_ms(CONF_NET_SSID, CONF_NET_PASS, CONF_NET_AUTH, CONF_NET_CONN_TIMEOUT))
+ // panic("cyw43_arch_wifi_connect failed\n");
+
+ printf("connected to Wi-Fi\n");
+
+ // TODO: announce hostname(?)
+}
+
+static void async_init() {
+ init_cyw34();
+ init_i2c();
+ init_wifi();
+
+ xEventGroupSetBits(init_complete, 1);
+
+ // delete self
+ vTaskDelete(NULL);
+}
+
+void init() {
+ init_complete = xEventGroupCreate();
+
+ // used for debug `printf` and `panic` on errors
+ init_stdio();
+
+ // defer other initialization until the task scheduler is running (important)
+ xTaskCreate((TaskFunction_t) async_init, "init", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 4, NULL);
+}
+
+void await_init() {
+ xEventGroupWaitBits(init_complete, 1, pdFALSE, pdFALSE, portMAX_DELAY);
+}
+
diff --git a/main/init.h b/main/init.h
new file mode 100644
index 0000000..de9023c
--- /dev/null
+++ b/main/init.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <FreeRTOS.h>
+#include <event_groups.h>
+
+/**
+ * \brief init function complete event group handle
+ *
+ * This is required to make sure the main task waits until initialization is
+ * complete. Due to the combination of FreeRTOS + lwIP, the initialization
+ * should be done while the task scheduler is running. Specifically the
+ * cyw43_arch_init functions make the pico hang indefinitely when used while
+ * the task scheduler is not running.
+ *
+ * \note `init_complete` only utilizes LSB, so `uxBitsToWaitFor` should always
+ * be set to *1*
+ */
+extern EventGroupHandle_t init_complete;
+
+/**
+ * \brief initialize all peripherals on the pico
+ *
+ * This function only synchronously initializes the standard input/output (used
+ * for `printf` and `panic`), and queues all other types of initialization in
+ * the `init` task using FreeRTOS.
+ *
+ * \note Tasks dependent on the wifi being initialized should use the
+ * `init_complete` event group to wait for initialization to complete!
+ */
+void init();
+
+/**
+ * \brief block task until all initialization is complete
+ *
+ * utility function, see above comments
+ */
+void await_init();
+
diff --git a/main/lib b/main/lib
new file mode 120000
index 0000000..dc598c5
--- /dev/null
+++ b/main/lib
@@ -0,0 +1 @@
+../lib \ No newline at end of file
diff --git a/main/lib/pico-sdk b/main/lib/pico-sdk
deleted file mode 160000
-Subproject 6a7db34ff63345a7badec79ebea3aaef1712f37
diff --git a/main/lwipopts.h b/main/lwipopts.h
index 75a57ee..b2b6e76 100644
--- a/main/lwipopts.h
+++ b/main/lwipopts.h
@@ -1,13 +1,7 @@
#pragma once
-// allow override in some examples
-#ifndef NO_SYS
-#define NO_SYS 1
-#endif
-
-#ifndef LWIP_SOCKET
-#define LWIP_SOCKET 0
-#endif
+#define NO_SYS 0
+#define LWIP_SOCKET 1
#if PICO_CYW43_ARCH_POLL
#define MEM_LIBC_MALLOC 1
@@ -83,3 +77,16 @@
#define SLIP_DEBUG LWIP_DBG_OFF
#define DHCP_DEBUG LWIP_DBG_OFF
+#define TCPIP_THREAD_STACKSIZE 2048
+#define DEFAULT_THREAD_STACKSIZE 1024
+#define DEFAULT_RAW_RECVMBOX_SIZE 8
+#define TCPIP_MBOX_SIZE 8
+
+#define DEFAULT_UDP_RECVMBOX_SIZE TCPIP_MBOX_SIZE
+#define DEFAULT_TCP_RECVMBOX_SIZE TCPIP_MBOX_SIZE
+#define DEFAULT_ACCEPTMBOX_SIZE TCPIP_MBOX_SIZE
+
+#define LWIP_TIMEVAL_PRIVATE 0
+
+#define LWIP_TCPIP_CORE_LOCKING_INPUT 1
+
diff --git a/main/main.c b/main/main.c
new file mode 100644
index 0000000..19dd3cd
--- /dev/null
+++ b/main/main.c
@@ -0,0 +1,32 @@
+#include <FreeRTOS.h>
+#include <task.h>
+
+#include <pico/stdlib.h>
+#include <pico/time.h>
+
+#include "config.h"
+#include "init.h"
+#include "sock.h"
+#include "i2c.h"
+
+void blink_task() {
+ await_init(); // `blink_task` uses GPIO
+
+ while (true) {
+ cyw43_arch_gpio_put(LED_PIN, 0);
+ vTaskDelay(250 / portTICK_PERIOD_MS);
+ cyw43_arch_gpio_put(LED_PIN, 1);
+ vTaskDelay(250 / portTICK_PERIOD_MS);
+ }
+}
+
+int main() {
+ init();
+
+ xTaskCreate((TaskFunction_t) blink_task, "blink", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2, NULL);
+ //xTaskCreate((TaskFunction_t) serve_task, "serve", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2, NULL);
+ xTaskCreate((TaskFunction_t) bus_task, "bus", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2, NULL);
+
+ vTaskStartScheduler();
+}
+
diff --git a/main/main.cpp b/main/main.cpp
deleted file mode 100644
index 9fd3123..0000000
--- a/main/main.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#include <stdio.h>
-
-#include "config.h"
-
-#include "pico/stdlib.h"
-#include "pico/cyw43_arch.h"
-
-const unsigned int LED_PIN = CYW43_WL_GPIO_LED_PIN;
-
-int main() {
- stdio_init_all();
- sleep_ms(2000);
-
- if (cyw43_arch_init_with_country(CONF_NET_COUNTRY)) {
- printf("failed to initialize\n");
- return 1;
- }
- cyw43_arch_gpio_put(LED_PIN, 1);
- printf("initialised\n");
-
- cyw43_arch_enable_sta_mode();
-
- if (cyw43_arch_wifi_connect_timeout_ms(CONF_NET_SSID, CONF_NET_PASS, CYW43_AUTH_WPA2_AES_PSK, 10000)) {
- printf("failed to connect\n");
- return 1;
- }
- printf("connected\n");
-
- while (true) {
- cyw43_arch_gpio_put(LED_PIN, 0);
- sleep_ms(250);
- cyw43_arch_gpio_put(LED_PIN, 1);
- sleep_ms(250);
- }
-}
-
diff --git a/main/makefile b/main/makefile
new file mode 100644
index 0000000..9df4f09
--- /dev/null
+++ b/main/makefile
@@ -0,0 +1,9 @@
+TARGET = $(BUILD_DIR)/main.uf2
+
+include ../lazy.mk
+
+flash: $(TARGET) FORCE
+ picotool load -fx $<
+# -f forces a reboot of the pico before flashing
+# -x resets the pico after flashing
+
diff --git a/main/readme.md b/main/readme.md
new file mode 100644
index 0000000..425a00b
--- /dev/null
+++ b/main/readme.md
@@ -0,0 +1,21 @@
+# main controller firmware
+
+This directory contains the software for the main controller of the Puzzle Box.
+
+## building
+
+1. make sure the submodules are initialized
+2. copy [`config.def.h`](./config.def.h) to `config.h` and edit the defaults
+3. `mkdir build`
+4. `cmake -B build`
+5. `make -C build` or `ninja -C build` (choose your preference)
+
+alternatively, a makefile is provided for convenience
+
+## "flashing"
+
+1. [build](#building)
+2. (re)connect the raspberry pi pico while holding the BOOTSEL button (this is
+ the only button)
+3. `picotool load build/main.uf2`
+
diff --git a/main/sock.c b/main/sock.c
new file mode 100644
index 0000000..fe932bb
--- /dev/null
+++ b/main/sock.c
@@ -0,0 +1,96 @@
+#include <pico/stdio.h>
+
+#include <lwip/opt.h>
+#include <lwip/sys.h>
+#include <lwip/api.h>
+#include <string.h>
+
+#include "init.h"
+#include "config.h"
+#include "i2ctcpv1.h"
+#include "sock.h"
+
+struct netconn* current_connection = NULL;
+i2ctcp_msg_t recv_msg;
+
+void i2c_send(uint16_t addr, const char * data, size_t data_size) {
+ if (current_connection == NULL) return;
+
+ i2ctcp_msg_t send_msg = {
+ .addr = addr,
+ .data = (char *) data,
+ .length = data_size,
+ };
+
+ char * buf;
+ size_t buf_sz;
+
+ if (!i2ctcp_write(&send_msg, &buf, &buf_sz)) return;
+
+ // NOTE: netconn does return an error code, but the data needs to be freed
+ // whether netconn throws an error or not, so it remains unused
+ netconn_write(current_connection, buf, buf_sz, NETCONN_COPY);
+
+ free(buf);
+}
+
+void i2c_recv(uint16_t addr, const char * data, size_t data_size) {
+ printf("address: 0x%02x\n", addr);
+ printf("data: \"%.*s\"\n", data_size, data);
+
+ // send message back
+ char reply[] = "Test message back!";
+ i2c_send(0x69, reply, strlen(reply));
+
+ // TODO: this function should forward the recieved message onto the puzzle
+ // bus instead of printing/replying
+}
+
+void recv_handler(struct netconn* conn, struct netbuf* buf) {
+ i2ctcp_read_reset(&recv_msg);
+
+ do {
+ char* data;
+ uint16_t len;
+ netbuf_data(buf, (void**)&data, &len);
+
+ // continue early if more data is needed to complete message
+ if (!i2ctcp_read(&recv_msg, data, len)) continue;
+
+ // forward received message to puzzle bus
+ i2c_recv(recv_msg.addr, recv_msg.data, recv_msg.length);
+ free(recv_msg.data);
+ } while (netbuf_next(buf) >= 0);
+
+ netbuf_delete(buf);
+}
+
+void accept_handler(struct netconn* conn) {
+ current_connection = conn;
+
+ struct netbuf* buf;
+ while (netconn_recv(conn, &buf) == ERR_OK)
+ recv_handler(conn, buf);
+
+ netconn_close(conn);
+ netconn_delete(conn);
+
+ current_connection = NULL;
+}
+
+void serve_task() {
+ await_init();
+
+ printf("starting server...\n");
+ struct netconn* conn = netconn_new(NETCONN_TCP);
+ netconn_bind(conn, IP_ADDR_ANY, CONF_SRV_PORT);
+ netconn_listen(conn);
+
+ printf("listening on %s:%d\n", ip4addr_ntoa(netif_ip4_addr(netif_list)), CONF_SRV_PORT);
+ while (1) {
+ struct netconn* incoming;
+ if (netconn_accept(conn, &incoming) == ERR_OK)
+ accept_handler(incoming);
+ }
+}
+
diff --git a/main/sock.h b/main/sock.h
new file mode 100644
index 0000000..f2db35d
--- /dev/null
+++ b/main/sock.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h>
+
+/** \brief start listening for TCP socket requests */
+void serve_task();
+
+void i2c_send(uint16_t addr, const char * data, size_t data_size);
+void i2c_recv(uint16_t addr, const char * data, size_t data_size);
+