diff --git a/examples/tutorials/thread_network/01_sensor_ipc/main.c b/examples/tutorials/thread_network/01_sensor_ipc/main.c deleted file mode 100644 index 20acf7871..000000000 --- a/examples/tutorials/thread_network/01_sensor_ipc/main.c +++ /dev/null @@ -1,33 +0,0 @@ -#include - -#include -#include -#include -#include - -// Global variable storing the current temperature. This is written to in the -// main loop, and read from in the IPC handler. Because the app is single -// threaded and has no yield point when writing the value, we do not need to -// worry about synchronization -- reads never happen during a write. -static int current_temperature = 0; - -// static void sensor_ipc_callback(int pid, int len, int buf, -// __attribute__((unused)) void *ud) -// { -// } - -int main(void) { - // Measure the temperature once before registering ourselves as an IPC - // service. This ensures that we always return a correct (but potentially - // stale) temperature value. - libtocksync_temperature_read(¤t_temperature); - - // We measure the temperature in the main loop and simply provide the latest - // reading in an IPC. This means that the control app does not have to wait - // for the temperature read system call to complete. - while (1) { - libtocksync_temperature_read(¤t_temperature); - // printf("Current temperature: %d\r\n", current_temperature); - libtocksync_alarm_delay_ms(1000); - } -} diff --git a/examples/tutorials/thread_network/01_sensor_ipc/Makefile b/examples/tutorials/thread_network/01_sensor_temperature/Makefile similarity index 100% rename from examples/tutorials/thread_network/01_sensor_ipc/Makefile rename to examples/tutorials/thread_network/01_sensor_temperature/Makefile diff --git a/examples/tutorials/thread_network/01_sensor_temperature/main.c b/examples/tutorials/thread_network/01_sensor_temperature/main.c new file mode 100644 index 000000000..0954e7d7c --- /dev/null +++ b/examples/tutorials/thread_network/01_sensor_temperature/main.c @@ -0,0 +1,21 @@ +#include + +#include +#include +#include +#include + +static int current_temperature = 0; + +int main(void) { + // Measure temperature -- returned in the form 2200 => 22C + libtocksync_temperature_read(¤t_temperature); + + // Convert temperature + int whole_degree = current_temperature / 100; + int decimal_degree = current_temperature % 100; + + printf("Hello World, the temperature is: %i.%i\r\n", whole_degree, decimal_degree); + + return 0; +} diff --git a/examples/tutorials/thread_network/02_sensor_final/main.c b/examples/tutorials/thread_network/02_sensor_final/main.c index 374489d16..45edc22cd 100644 --- a/examples/tutorials/thread_network/02_sensor_final/main.c +++ b/examples/tutorials/thread_network/02_sensor_final/main.c @@ -5,47 +5,23 @@ #include #include -// Global variable storing the current temperature. This is written to in the -// main loop, and read from in the IPC handler. Because the app is single -// threaded and has no yield point when writing the value, we do not need to -// worry about synchronization -- reads never happen during a write. static int current_temperature = 0; -static void sensor_ipc_callback(int pid, int len, int buf, - __attribute__((unused)) void* ud) { - // A client has requested us to provide them the current temperature value. - // We must make sure that it provides us with a buffer sufficiently large to - // store a single integer: - if (len < ((int) sizeof(current_temperature))) { - // We do not inform the caller and simply return. We do print a log message: - puts("[thread-sensor] ERROR: sensor IPC invoked with too small buffer.\r\n"); - return; - } - - // The buffer is large enough, copy the current temperature into it: - memcpy((void*) buf, ¤t_temperature, sizeof(current_temperature)); - - // Let the client know: - ipc_notify_client(pid); -} - int main(void) { - // Measure the temperature once before registering ourselves as an IPC - // service. This ensures that we always return a correct (but potentially - // stale) temperature value. - libtocksync_temperature_read(¤t_temperature); - // Register this application as an IPC service under its name: - ipc_register_service_callback("org.tockos.thread-tutorial.sensor", - sensor_ipc_callback, - NULL); - - // We measure the temperature in the main loop and simply provide the latest - // reading in an IPC. This means that the control app does not have to wait - // for the temperature read system call to complete. + // We measure the temperature in the main loop and + // print this value to the console. while (1) { + // Measure temperature -- returned in the form 2200 => 22C libtocksync_temperature_read(¤t_temperature); - // printf("Current temperature: %d\r\n", current_temperature); + + // Convert temperature + int whole_degree = current_temperature / 100; + int decimal_degree = current_temperature % 100; + + printf("Current temperature: %i.%i\r\n", whole_degree, decimal_degree); + + // Delay 1000 ms (1 second). libtocksync_alarm_delay_ms(1000); } } diff --git a/examples/tutorials/thread_network/03_controller_screen/main.c b/examples/tutorials/thread_network/03_controller_screen/main.c deleted file mode 100644 index c9cbb155a..000000000 --- a/examples/tutorials/thread_network/03_controller_screen/main.c +++ /dev/null @@ -1,138 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -size_t sensor_svc_num = 0; - -uint8_t global_temperature_setpoint = 0; -uint8_t prior_global_temperature_setpoint = 255; - -uint8_t local_temperature_setpoint = 22; -uint8_t prior_local_temperature_setpoint = 255; - -int measured_temperature = 0; -int prior_measured_temperature = 0; - -// Callback event indicator -bool callback_event = false; - -libtock_alarm_t read_temperature_timer; - -// We use this variable as a buffer that is naturally aligned to the int -// alignment, and has an alignment >= its size. -uint8_t temperature_buffer[64] __attribute__((aligned(64))); - -static void update_screen(void); -static int init_controller_ipc(void); - - -static void read_temperature_timer_callback(__attribute__ ((unused)) uint32_t now, - __attribute__ ((unused)) uint32_t scheduled, - __attribute__ ((unused)) void* opaque) { - // Request a new temperature reading from the sensor: - ipc_notify_service(sensor_svc_num); -} - - -static void sensor_callback(__attribute__ ((unused)) int pid, - __attribute__ ((unused)) int len, - __attribute__ ((unused)) int arg2, - __attribute__ ((unused)) void* ud) { - // update measured temperature - measured_temperature = *((int*) &temperature_buffer[0]); - - // Indicate that we have received a callback. - callback_event = true; - - // Request a new temperature reading in 250ms: - libtock_alarm_in_ms(250, read_temperature_timer_callback, NULL, &read_temperature_timer); -} - -static void button_callback(returncode_t ret, - int btn_num, - bool pressed) { - if (ret != RETURNCODE_SUCCESS) return; - - if (pressed) { - if (btn_num == 0 && local_temperature_setpoint < 35) { - local_temperature_setpoint++; - } else if (btn_num == 1 && local_temperature_setpoint > 0) { - local_temperature_setpoint--; - } else if (btn_num == 2) { - local_temperature_setpoint = 22; - } - } - - // Indicate that we have received a callback. - callback_event = true; - - return; -} - -int main(void) { - int err; - int i; - - init_controller_ipc(); - - // Enable buttons - for (i = 0; i < 3; i++) { - err = libtock_button_notify_on_press(i, button_callback); - if (err < 0) return err; - } - - ipc_notify_service(sensor_svc_num); - - for ( ;;) { - callback_event = false; - yield_for(&callback_event); - - if (measured_temperature != prior_measured_temperature - || global_temperature_setpoint != prior_global_temperature_setpoint - || local_temperature_setpoint != prior_local_temperature_setpoint) { - prior_measured_temperature = measured_temperature; - prior_global_temperature_setpoint = global_temperature_setpoint; - prior_local_temperature_setpoint = local_temperature_setpoint; - update_screen(); - } - } -} - -static int init_controller_ipc(void) { - int err = -1; - int discover_retry_count = 0; - int err_sensor = -1; - - while (err_sensor < 0 && discover_retry_count < 100) { - err_sensor = ipc_discover("org.tockos.thread-tutorial.sensor", &sensor_svc_num); - discover_retry_count++; - if (err < 0) { - libtocksync_alarm_delay_ms(10); - } - } - - if (err_sensor < 0) { - printf("No sensor service\r\n"); - return -1; - } - - printf("[controller] Discovered sensor service: %d\r\n", sensor_svc_num); - - ipc_register_client_callback(sensor_svc_num, sensor_callback, NULL); - ipc_share(sensor_svc_num, &temperature_buffer, sizeof(temperature_buffer)); - - return err; -} - -static void update_screen(void) { - printf("[controller] TODO: update screen! Measured temperature: %d.%02d\r\n", - measured_temperature / 100, measured_temperature % 100); -} diff --git a/examples/tutorials/thread_network/06_openthread/Makefile b/examples/tutorials/thread_network/03_openthread/Makefile similarity index 100% rename from examples/tutorials/thread_network/06_openthread/Makefile rename to examples/tutorials/thread_network/03_openthread/Makefile diff --git a/examples/tutorials/thread_network/03_openthread/main.c b/examples/tutorials/thread_network/03_openthread/main.c new file mode 100644 index 000000000..6e86a3d3d --- /dev/null +++ b/examples/tutorials/thread_network/03_openthread/main.c @@ -0,0 +1,148 @@ +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// helper utility demonstrating network config setup +static void __attribute__((unused)) setNetworkConfiguration(otInstance* aInstance); + +// callback for Thread state change events +static void stateChangeCallback(uint32_t flags, void* context); + +// helper utility to print ip address +static void __attribute__((unused)) print_ip_addr(otInstance* instance); + +int main(__attribute__((unused)) int argc, __attribute__((unused)) char* argv[]) { + // Initialize OpenThread instance. + otSysInit(argc, argv); + otInstance* instance; + instance = otInstanceInitSingle(); + assert(instance); + + // set child timeout to 60 seconds. + otThreadSetChildTimeout(instance, 60); + + // Set callback to be notified when thread state changes. + otSetStateChangedCallback(instance, stateChangeCallback, instance); + + /////////////////////////////////////////////////// + // THREAD NETWORK SETUP HERE + + // TODO: Configure network. + + // TODO: Enable network interface. + + // TODO: Start Thread network. + + // + //////////////////////////////////////////////////// + + // OpenThread main loop. + for ( ;;) { + // Execute any pending OpenThread related work. + otTaskletsProcess(instance); + + // Execute any platform related work (e.g. check + // radio buffer for new packets). + otSysProcessDrivers(instance); + + // If there is not pending platform or OpenThread + // related work -- yield. + if (!otTaskletsArePending(instance) && + !openthread_platform_pending_work()) { + yield(); + } + + } + + return 0; +} + +// Helper method that configures the OpenThread network dataset +// for the desired tutorial configuration. +// We set the following dataset parameters: +// -- Channel: 26 +// -- PanId: 0xabcd +// -- Networkkey: 00112233445566778899aabbccddeeff +void setNetworkConfiguration(otInstance* aInstance) { + otOperationalDataset aDataset; + + memset(&aDataset, 0, sizeof(otOperationalDataset)); + + /* Set Channel to 26 */ + aDataset.mChannel = 26; + aDataset.mComponents.mIsChannelPresent = true; + + /* Set Pan ID to abcd */ + aDataset.mPanId = (otPanId)0xabcd; + aDataset.mComponents.mIsPanIdPresent = true; + + /* Set network key to 00112233445566778899aabbccddeeff */ + uint8_t key[OT_NETWORK_KEY_SIZE] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + memcpy(aDataset.mNetworkKey.m8, key, sizeof(aDataset.mNetworkKey)); + aDataset.mComponents.mIsNetworkKeyPresent = true; + + otError error = otDatasetSetActive(aInstance, &aDataset); + assert(error == 0); + +} + +// Helper method that registers a stateChangeCallback to print +// when state changes occur (useful for debugging). +static void stateChangeCallback(uint32_t flags, void* context) { + otInstance* instance = (otInstance*)context; + if (!(flags & OT_CHANGED_THREAD_ROLE)) { + return; + } + + switch (otThreadGetDeviceRole(instance)) { + case OT_DEVICE_ROLE_DISABLED: + printf("[State Change] - Disabled.\n"); + break; + case OT_DEVICE_ROLE_DETACHED: + printf("[State Change] - Detached.\n"); + break; + case OT_DEVICE_ROLE_CHILD: + printf("[State Change] - Child.\n"); + printf("Successfully attached to Thread network as a child.\n"); + break; + case OT_DEVICE_ROLE_ROUTER: + printf("[State Change] - Router.\n"); + break; + case OT_DEVICE_ROLE_LEADER: + printf("[State Change] - Leader.\n"); + break; + default: + break; + } +} + +// Helper method to print the given Thread node's registered +// ipv6 address. +static void print_ip_addr(otInstance* instance) { + char addr_string[64]; + const otNetifAddress* unicastAddrs = otIp6GetUnicastAddresses(instance); + + printf("[THREAD] Device IPv6 Addresses: "); + for (const otNetifAddress* addr = unicastAddrs; addr; addr = addr->mNext) { + const otIp6Address ip6_addr = addr->mAddress; + otIp6AddressToString(&ip6_addr, addr_string, sizeof(addr_string)); + printf("%s\n", addr_string); + } +} diff --git a/examples/tutorials/thread_network/04_controller_thread/main.c b/examples/tutorials/thread_network/04_controller_thread/main.c deleted file mode 100644 index 440b58a26..000000000 --- a/examples/tutorials/thread_network/04_controller_thread/main.c +++ /dev/null @@ -1,207 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -u8g2_t u8g2; - -size_t sensor_svc_num = 0; -// size_t openthread_svc_num = 0; - -uint8_t global_temperature_setpoint = 0; -uint8_t prior_global_temperature_setpoint = 255; - -uint8_t local_temperature_setpoint = 22; -uint8_t prior_local_temperature_setpoint = 255; - -int measured_temperature = 0; -int prior_measured_temperature = 0; - -bool network_up = false; - -// Callback event indicator -bool callback_event = false; - -libtock_alarm_t read_temperature_timer; -// libtock_alarm_t network_timer; - -// We use this variable as a buffer that is naturally aligned to the int -// alignment, and has an alignment >= its size. -uint8_t temperature_buffer[64] __attribute__((aligned(64))); -// uint8_t openthread_buffer[64] __attribute__((aligned(64))); - -static void update_screen(void); -static int init_controller_ipc(void); - - -static void read_temperature_timer_callback(__attribute__ ((unused)) uint32_t now, - __attribute__ ((unused)) uint32_t scheduled, - __attribute__ ((unused)) void* opaque) { - // Request a new temperature reading from the sensor: - ipc_notify_service(sensor_svc_num); -} - - -// static void update_network_timer_callback(__attribute__ ((unused)) uint32_t now, -// __attribute__ ((unused)) uint32_t scheduled, -// __attribute__ ((unused)) void* opaque) { -// openthread_buffer[0] = local_temperature_setpoint; -// ipc_notify_service(openthread_svc_num); -// libtock_alarm_in_ms(250, update_network_timer_callback, NULL, &network_timer); -// -// } - -static void sensor_callback(__attribute__ ((unused)) int pid, - __attribute__ ((unused)) int len, - __attribute__ ((unused)) int arg2, - __attribute__ ((unused)) void* ud) { - // update measured temperature - measured_temperature = *((int*) &temperature_buffer[0]); - - // Indicate that we have received a callback. - callback_event = true; - - // Request a new temperature reading in 250ms: - libtock_alarm_in_ms(250, read_temperature_timer_callback, NULL, &read_temperature_timer); -} - -// static void openthread_callback( __attribute__ ((unused)) int pid, -// __attribute__ ((unused)) int len, -// __attribute__ ((unused)) int arg2, -// __attribute__ ((unused)) void* ud) { -// network_up = true; -// -// // update setpoint temperature -// global_temperature_setpoint = *((int*) &openthread_buffer[0]); -// -// // Indicate that we have received a callback. -// callback_event = true; -// -// } - -static void button_callback(returncode_t ret, - int btn_num, - bool pressed) { - if (ret != RETURNCODE_SUCCESS) return; - - if (pressed) { - if (btn_num == 0 && local_temperature_setpoint < 35) { - local_temperature_setpoint++; - } else if (btn_num == 1 && local_temperature_setpoint > 0) { - local_temperature_setpoint--; - } else if (btn_num == 2) { - local_temperature_setpoint = 22; - } - } - - // Indicate that we have received a callback. - callback_event = true; - - return; -} - -int main(void) { - int err; - int i; - - u8g2_tock_init(&u8g2); - u8g2_SetFont(&u8g2, u8g2_font_profont12_tr); - u8g2_SetFontPosTop(&u8g2); - - init_controller_ipc(); - - // Enable buttons - for (i = 0; i < 3; i++) { - err = libtock_button_notify_on_press(i, button_callback); - if (err < 0) return err; - } - - ipc_notify_service(sensor_svc_num); - // libtock_alarm_in_ms(500, update_network_timer_callback, NULL, &network_timer); - - for ( ;;) { - callback_event = false; - yield_for(&callback_event); - - if (measured_temperature != prior_measured_temperature - || global_temperature_setpoint != prior_global_temperature_setpoint - || local_temperature_setpoint != prior_local_temperature_setpoint) { - prior_measured_temperature = measured_temperature; - prior_global_temperature_setpoint = global_temperature_setpoint; - prior_local_temperature_setpoint = local_temperature_setpoint; - update_screen(); - } - } -} - -static int init_controller_ipc(void) { - int err = -1; - int discover_retry_count = 0; - int err_sensor = -1; - // int err_openthread = -1; - - // while (err_sensor < 0 && err_openthread < 0 && discover_retry_count < 100) { - while (err_sensor < 0 && discover_retry_count < 100) { - err_sensor = ipc_discover("org.tockos.thread-tutorial.sensor", &sensor_svc_num); - // err_openthread = ipc_discover("org.tockos.thread-tutorial.openthread", &openthread_svc_num); - discover_retry_count++; - if (err < 0) { - libtocksync_alarm_delay_ms(10); - } - } - - if (err_sensor < 0) { - printf("No sensor service\r\n"); - return -1; - } - - // if (err_openthread < 0) { - // printf("No openthread service\r\n"); - // return -1; - // } - - printf("[controller] Discovered sensor service: %d\r\n", sensor_svc_num); - // printf("[controller] Discovered openthread service: %d\r\n", openthread_svc_num); - - ipc_register_client_callback(sensor_svc_num, sensor_callback, NULL); - // ipc_register_client_callback(openthread_svc_num, openthread_callback, NULL); - - ipc_share(sensor_svc_num, &temperature_buffer, sizeof(temperature_buffer)); - // ipc_share(openthread_svc_num, &openthread_buffer, sizeof(openthread_buffer)); - - return err; -} - -static void update_screen(void) { - char temperature_set_point_str[35]; - char temperature_global_set_point_str[35]; - char temperature_current_measure_str[35]; - sprintf(temperature_set_point_str, "Set Point: %d", local_temperature_setpoint); - - if (network_up) { - sprintf(temperature_global_set_point_str, "Global Set Point: %d", global_temperature_setpoint); - } else { - sprintf(temperature_global_set_point_str, "Global Set Point: N/A"); - } - - // print measured temperature as value XX.25 - sprintf(temperature_current_measure_str, "Measured Temp: %d.%02d", measured_temperature / 100, - measured_temperature % 100); - - u8g2_ClearBuffer(&u8g2); - u8g2_SetDrawColor(&u8g2, 1); - u8g2_DrawStr(&u8g2, 0, 0, temperature_set_point_str); - u8g2_DrawStr(&u8g2, 0, 25, temperature_global_set_point_str); - u8g2_DrawStr(&u8g2, 0, 50, temperature_current_measure_str); - u8g2_SendBuffer(&u8g2); -} diff --git a/examples/tutorials/thread_network/07_openthread_final/Makefile b/examples/tutorials/thread_network/04_openthread_attach/Makefile similarity index 93% rename from examples/tutorials/thread_network/07_openthread_final/Makefile rename to examples/tutorials/thread_network/04_openthread_attach/Makefile index faacede32..5c487ec99 100644 --- a/examples/tutorials/thread_network/07_openthread_final/Makefile +++ b/examples/tutorials/thread_network/04_openthread_attach/Makefile @@ -1,7 +1,7 @@ # Makefile for user application # Specify this directory relative to the current application. -TOCK_USERLAND_BASE_DIR = ../../../../ +TOCK_USERLAND_BASE_DIR = ../../../.. # Which files to compile. C_SRCS := $(wildcard *.c) diff --git a/examples/tutorials/thread_network/04_openthread_attach/main.c b/examples/tutorials/thread_network/04_openthread_attach/main.c new file mode 100644 index 000000000..4d87dd654 --- /dev/null +++ b/examples/tutorials/thread_network/04_openthread_attach/main.c @@ -0,0 +1,160 @@ +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// helper utility demonstrating network config setup +static void setNetworkConfiguration(otInstance* aInstance); + +// callback for Thread state change events +static void stateChangeCallback(uint32_t flags, void* context); + +// helper utility to print ip address +static void print_ip_addr(otInstance* instance); + +int main(__attribute__((unused)) int argc, __attribute__((unused)) char* argv[]) { + // Initialize OpenThread instance. + otSysInit(argc, argv); + otInstance* instance; + instance = otInstanceInitSingle(); + assert(instance); + + // set child timeout to 60 seconds. + otThreadSetChildTimeout(instance, 60); + + // Set callback to be notified when thread state changes. + otSetStateChangedCallback(instance, stateChangeCallback, instance); + + /////////////////////////////////////////////////// + // THREAD NETWORK SETUP HERE + + // Configure network. + setNetworkConfiguration(instance); + + // Enable network interface. + while (otIp6SetEnabled(instance, true) != OT_ERROR_NONE) { + printf("Failed to start Thread network interface!\n"); + libtocksync_alarm_delay_ms(100); + } + + // Print IPv6 address. + print_ip_addr(instance); + + // Start Thread network. + while (otThreadSetEnabled(instance, true) != OT_ERROR_NONE) { + printf("Failed to start Thread stack!\n"); + libtocksync_alarm_delay_ms(100); + } + + // + //////////////////////////////////////////////////// + + // OpenThread main loop. + for ( ;;) { + // Execute any pending OpenThread related work. + otTaskletsProcess(instance); + + // Execute any platform related work (e.g. check + // radio buffer for new packets). + otSysProcessDrivers(instance); + + // If there is not pending platform or OpenThread + // related work -- yield. + if (!otTaskletsArePending(instance) && + !openthread_platform_pending_work()) { + yield(); + } + + } + + return 0; +} + +// Helper method that configures the OpenThread network dataset +// for the desired tutorial configuration. +// We set the following dataset parameters: +// -- Channel: 26 +// -- PanId: 0xabcd +// -- Networkkey: 00112233445566778899aabbccddeeff +void setNetworkConfiguration(otInstance* aInstance) { + otOperationalDataset aDataset; + + memset(&aDataset, 0, sizeof(otOperationalDataset)); + + /* Set Channel to 26 */ + aDataset.mChannel = 26; + aDataset.mComponents.mIsChannelPresent = true; + + /* Set Pan ID to abcd */ + aDataset.mPanId = (otPanId)0xabcd; + aDataset.mComponents.mIsPanIdPresent = true; + + /* Set network key to 00112233445566778899aabbccddeeff */ + uint8_t key[OT_NETWORK_KEY_SIZE] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + memcpy(aDataset.mNetworkKey.m8, key, sizeof(aDataset.mNetworkKey)); + aDataset.mComponents.mIsNetworkKeyPresent = true; + + otError error = otDatasetSetActive(aInstance, &aDataset); + assert(error == 0); + +} + +// Helper method that registers a stateChangeCallback to print +// when state changes occur (useful for debugging). +static void stateChangeCallback(uint32_t flags, void* context) { + otInstance* instance = (otInstance*)context; + if (!(flags & OT_CHANGED_THREAD_ROLE)) { + return; + } + + switch (otThreadGetDeviceRole(instance)) { + case OT_DEVICE_ROLE_DISABLED: + printf("[State Change] - Disabled.\n"); + break; + case OT_DEVICE_ROLE_DETACHED: + printf("[State Change] - Detached.\n"); + break; + case OT_DEVICE_ROLE_CHILD: + printf("[State Change] - Child.\n"); + printf("Successfully attached to Thread network as a child.\n"); + break; + case OT_DEVICE_ROLE_ROUTER: + printf("[State Change] - Router.\n"); + break; + case OT_DEVICE_ROLE_LEADER: + printf("[State Change] - Leader.\n"); + break; + default: + break; + } +} + +// Helper method to print the given Thread node's registered +// ipv6 address. +static void print_ip_addr(otInstance* instance) { + char addr_string[64]; + const otNetifAddress* unicastAddrs = otIp6GetUnicastAddresses(instance); + + printf("[THREAD] Device IPv6 Addresses: "); + for (const otNetifAddress* addr = unicastAddrs; addr; addr = addr->mNext) { + const otIp6Address ip6_addr = addr->mAddress; + otIp6AddressToString(&ip6_addr, addr_string, sizeof(addr_string)); + printf("%s\n", addr_string); + } +} diff --git a/examples/tutorials/thread_network/screen/openthread_app/Makefile b/examples/tutorials/thread_network/05_openthread_final/Makefile similarity index 93% rename from examples/tutorials/thread_network/screen/openthread_app/Makefile rename to examples/tutorials/thread_network/05_openthread_final/Makefile index de74ba3aa..5c487ec99 100644 --- a/examples/tutorials/thread_network/screen/openthread_app/Makefile +++ b/examples/tutorials/thread_network/05_openthread_final/Makefile @@ -1,7 +1,7 @@ # Makefile for user application # Specify this directory relative to the current application. -TOCK_USERLAND_BASE_DIR = ../../../../../ +TOCK_USERLAND_BASE_DIR = ../../../.. # Which files to compile. C_SRCS := $(wildcard *.c) diff --git a/examples/tutorials/thread_network/06_openthread/main.c b/examples/tutorials/thread_network/05_openthread_final/main.c similarity index 66% rename from examples/tutorials/thread_network/06_openthread/main.c rename to examples/tutorials/thread_network/05_openthread_final/main.c index 2398024cc..f19ab6cab 100644 --- a/examples/tutorials/thread_network/06_openthread/main.c +++ b/examples/tutorials/thread_network/05_openthread_final/main.c @@ -18,39 +18,17 @@ #include #include -#define UDP_PORT 1212 -static const char UDP_ROUTER_MULTICAST[] = "ff02::2"; +// helper utility demonstrating network config setup +static void setNetworkConfiguration(otInstance* aInstance); static otUdpSocket sUdpSocket; -static void initUdp(otInstance* aInstance); -static void sendUdp(otInstance* aInstance); - -uint8_t local_temperature_setpoint = 22; -uint8_t global_temperature_setpoint = 255; -uint8_t prior_global_temperature_setpoint = 255; -bool network_up = false; -bool send_local_temp = false; - -// Callback method for received udp packets. -static void handleUdpReceive(void* aContext, otMessage* aMessage, - const otMessageInfo* aMessageInfo); -__attribute__ ((unused)) -static void openthread_ipc_callback(__attribute__ ((unused)) int pid, - __attribute__ ((unused)) int len, - __attribute__ ((unused)) int buf, - __attribute__ ((unused)) void* ud) { - // TODO // - - // error check provided buffer is of length at least equal to 1 - - // copy value in buffer to local_temperature_setpoint - - // copy the value of the global_temperature_setpoint into the - // buffer and notify client only if the network is up -} -// helper utility demonstrating network config setup -static void setNetworkConfiguration(otInstance* aInstance); +void initUdp(otInstance* aInstance); + +void handleUdpRecvTemperature(void* aContext, otMessage* aMessage, + const otMessageInfo* aMessageInfo); + +void sendUdpTemperature(otInstance* aInstance, uint8_t temperature); // callback for Thread state change events static void stateChangeCallback(uint32_t flags, void* context); @@ -65,55 +43,52 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char* argv[]) instance = otInstanceInitSingle(); assert(instance); - // TODO: - // Register this application as an IPC service under its name - // "org.tockos.thread-tutorial.openthread". + // set child timeout to 60 seconds. + otThreadSetChildTimeout(instance, 60); + + // Set callback to be notified when thread state changes. + otSetStateChangedCallback(instance, stateChangeCallback, instance); - /* As part of the initialization, we will: - - Init dataset with the following properties: - - channel: 26 - - network key: 0x00112233445566778899aabbccddeeff - - PAN ID: 0xabcd - - configure ip addr (ifconfig up) - - start thread network (thread start) - */ + /////////////////////////////////////////////////// + // THREAD NETWORK SETUP HERE + // Configure network. setNetworkConfiguration(instance); - // set child timeout to 60 seconds. - otThreadSetChildTimeout(instance, 60); + // Init UDP interface. + initUdp(instance); - /* Start the Thread network interface (CLI cmd -> ifconfig up) */ + // Enable network interface. while (otIp6SetEnabled(instance, true) != OT_ERROR_NONE) { printf("Failed to start Thread network interface!\n"); libtocksync_alarm_delay_ms(100); } - // Set callback to be notified when thread state changes. - otSetStateChangedCallback(instance, stateChangeCallback, instance); - - // Print to console the device's assigned IPV6 address. + // Print IPv6 address. print_ip_addr(instance); - // Initialize UDP socket (see guide: https://openthread.io/codelabs/openthread-apis#7) - initUdp(instance); - - /* Start the Thread stack (CLI cmd -> thread start) */ + // Start Thread network. while (otThreadSetEnabled(instance, true) != OT_ERROR_NONE) { printf("Failed to start Thread stack!\n"); libtocksync_alarm_delay_ms(100); } + // + //////////////////////////////////////////////////// + + // OpenThread main loop. for ( ;;) { + // Execute any pending OpenThread related work. otTaskletsProcess(instance); - otSysProcessDrivers(instance); - if (send_local_temp) { - sendUdp(instance); - send_local_temp = false; - } + // Execute any platform related work (e.g. check + // radio buffer for new packets). + otSysProcessDrivers(instance); - if (!otTaskletsArePending(instance) && !openthread_platform_pending_work()) { + // If there is not pending platform or OpenThread + // related work -- yield. + if (!otTaskletsArePending(instance) && + !openthread_platform_pending_work()) { yield(); } @@ -122,6 +97,12 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char* argv[]) return 0; } +// Helper method that configures the OpenThread network dataset +// for the desired tutorial configuration. +// We set the following dataset parameters: +// -- Channel: 26 +// -- PanId: 0xabcd +// -- Networkkey: 00112233445566778899aabbccddeeff void setNetworkConfiguration(otInstance* aInstance) { otOperationalDataset aDataset; @@ -146,6 +127,8 @@ void setNetworkConfiguration(otInstance* aInstance) { } +// Helper method that registers a stateChangeCallback to print +// when state changes occur (useful for debugging). static void stateChangeCallback(uint32_t flags, void* context) { otInstance* instance = (otInstance*)context; if (!(flags & OT_CHANGED_THREAD_ROLE)) { @@ -160,10 +143,9 @@ static void stateChangeCallback(uint32_t flags, void* context) { printf("[State Change] - Detached.\n"); break; case OT_DEVICE_ROLE_CHILD: - network_up = true; - sendUdp(instance); printf("[State Change] - Child.\n"); printf("Successfully attached to Thread network as a child.\n"); + sendUdpTemperature(instance, 22); break; case OT_DEVICE_ROLE_ROUTER: printf("[State Change] - Router.\n"); @@ -176,6 +158,8 @@ static void stateChangeCallback(uint32_t flags, void* context) { } } +// Helper method to print the given Thread node's registered +// ipv6 address. static void print_ip_addr(otInstance* instance) { char addr_string[64]; const otNetifAddress* unicastAddrs = otIp6GetUnicastAddresses(instance); @@ -188,8 +172,9 @@ static void print_ip_addr(otInstance* instance) { } } -void handleUdpReceive(void* aContext, otMessage* aMessage, - const otMessageInfo* aMessageInfo) { + +void handleUdpRecvTemperature(void* aContext, otMessage* aMessage, + const otMessageInfo* aMessageInfo) { OT_UNUSED_VARIABLE(aContext); OT_UNUSED_VARIABLE(aMessageInfo); char buf[2]; @@ -198,7 +183,7 @@ void handleUdpReceive(void* aContext, otMessage* aMessage, otIp6AddressToString(&sender_addr, buf, sizeof(buf)); otMessageRead(aMessage, otMessageGetOffset(aMessage), buf, sizeof(buf) - 1); - global_temperature_setpoint = buf[0]; + printf("Received UDP Packet: %d\r\n", buf[0]); } void initUdp(otInstance* aInstance) { @@ -207,13 +192,13 @@ void initUdp(otInstance* aInstance) { memset(&sUdpSocket, 0, sizeof(sUdpSocket)); memset(&listenSockAddr, 0, sizeof(listenSockAddr)); - listenSockAddr.mPort = UDP_PORT; + listenSockAddr.mPort = 1212; - otUdpOpen(aInstance, &sUdpSocket, handleUdpReceive, aInstance); + otUdpOpen(aInstance, &sUdpSocket, handleUdpRecvTemperature, aInstance); otUdpBind(aInstance, &sUdpSocket, &listenSockAddr, OT_NETIF_THREAD); } -void sendUdp(otInstance* aInstance) { +void sendUdpTemperature(otInstance* aInstance, uint8_t temperature) { otError error = OT_ERROR_NONE; otMessage* message; @@ -222,9 +207,9 @@ void sendUdp(otInstance* aInstance) { memset(&messageInfo, 0, sizeof(messageInfo)); - otIp6AddressFromString(UDP_ROUTER_MULTICAST, &destinationAddr); + otIp6AddressFromString("ff02::02", &destinationAddr); messageInfo.mPeerAddr = destinationAddr; - messageInfo.mPeerPort = UDP_PORT; + messageInfo.mPeerPort = 1212; message = otUdpNewMessage(aInstance, NULL); if (message == NULL) { @@ -232,7 +217,7 @@ void sendUdp(otInstance* aInstance) { return; } - error = otMessageAppend(message, &local_temperature_setpoint, sizeof(local_temperature_setpoint)); + error = otMessageAppend(message, &temperature, 1); if (error != OT_ERROR_NONE && message != NULL) { printf("Error appending to udp message\n"); otMessageFree(message); diff --git a/examples/tutorials/thread_network/03_controller_screen/Makefile b/examples/tutorials/thread_network/06_screen/Makefile similarity index 91% rename from examples/tutorials/thread_network/03_controller_screen/Makefile rename to examples/tutorials/thread_network/06_screen/Makefile index 87ec7c58d..0748e329c 100644 --- a/examples/tutorials/thread_network/03_controller_screen/Makefile +++ b/examples/tutorials/thread_network/06_screen/Makefile @@ -6,7 +6,7 @@ TOCK_USERLAND_BASE_DIR = ../../../../ # Which files to compile. C_SRCS := $(wildcard *.c) -PACKAGE_NAME = thread_controller +PACKAGE_NAME = screen # Include userland master makefile. Contains rules and flags for actually # building the application. diff --git a/examples/tutorials/thread_network/06_screen/main.c b/examples/tutorials/thread_network/06_screen/main.c new file mode 100644 index 000000000..b7e237d3a --- /dev/null +++ b/examples/tutorials/thread_network/06_screen/main.c @@ -0,0 +1,6 @@ +#include +#include +#include +#include + +int main(void) {} diff --git a/examples/tutorials/thread_network/07_openthread_final/main.c b/examples/tutorials/thread_network/07_openthread_final/main.c deleted file mode 100644 index 82ec813a9..000000000 --- a/examples/tutorials/thread_network/07_openthread_final/main.c +++ /dev/null @@ -1,263 +0,0 @@ -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define UDP_PORT 1212 -static const char UDP_ROUTER_MULTICAST[] = "ff02::2"; - -static otUdpSocket sUdpSocket; -static void initUdp(otInstance* aInstance); -static void sendUdp(otInstance* aInstance); - -uint8_t local_temperature_setpoint = 22; -uint8_t global_temperature_setpoint = 255; -uint8_t prior_global_temperature_setpoint = 255; -bool network_up = false; -bool send_local_temp = false; - -// Callback method for received udp packets. -static void handleUdpReceive(void* aContext, otMessage* aMessage, - const otMessageInfo* aMessageInfo); - -static void openthread_ipc_callback(int pid, int len, int buf, - __attribute__((unused)) void* ud) { - // A client has requested us to provide them the current temperature value. - // We must make sure that it provides us with a buffer sufficiently large to - // store a single integer: - if (len < ((int) sizeof(prior_global_temperature_setpoint))) { - // We do not inform the caller and simply return. We do print a log message: - puts("[thread] ERROR: sensor IPC invoked with too small buffer.\r\n"); - } - - // copy value in buffer to local_temperature_setpoint - uint8_t passed_local_setpoint = *((uint8_t*) buf); - if (passed_local_setpoint != local_temperature_setpoint) { - // The local setpoint has changed, update it. - local_temperature_setpoint = passed_local_setpoint; - send_local_temp = true; - } - - if (network_up) { - if (prior_global_temperature_setpoint != global_temperature_setpoint) { - prior_global_temperature_setpoint = global_temperature_setpoint; - - // The buffer is large enough, copy the current temperature into it. - memcpy((void*) buf, &global_temperature_setpoint, sizeof(global_temperature_setpoint)); - - // Notify the client that the temperature has changed. - ipc_notify_client(pid); - } - } -} - -// helper utility demonstrating network config setup -static void setNetworkConfiguration(otInstance* aInstance); - -// callback for Thread state change events -static void stateChangeCallback(uint32_t flags, void* context); - -// helper utility to print ip address -static void print_ip_addr(otInstance* instance); - -int main(__attribute__((unused)) int argc, __attribute__((unused)) char* argv[]) { - // Initialize OpenThread instance. - otSysInit(argc, argv); - otInstance* instance; - instance = otInstanceInitSingle(); - assert(instance); - - // Register this application as an IPC service under its name: - ipc_register_service_callback("org.tockos.thread-tutorial.openthread", - openthread_ipc_callback, - NULL); - - /* As part of the initialization, we will: - - Init dataset with the following properties: - - channel: 26 - - network key: 0x00112233445566778899aabbccddeeff - - PAN ID: 0xabcd - - configure ip addr (ifconfig up) - - start thread network (thread start) - */ - - setNetworkConfiguration(instance); - - // set child timeout to 60 seconds - otThreadSetChildTimeout(instance, 60); - - /* Start the Thread network interface (CLI cmd -> ifconfig up) */ - while (otIp6SetEnabled(instance, true) != OT_ERROR_NONE) { - printf("Failed to start Thread network interface!\n"); - libtocksync_alarm_delay_ms(100); - } - - otSetStateChangedCallback(instance, stateChangeCallback, instance); - - print_ip_addr(instance); - - // Initialize UDP socket (see guide: https://openthread.io/codelabs/openthread-apis#7) - initUdp(instance); - - /* Start the Thread stack (CLI cmd -> thread start) */ - while (otThreadSetEnabled(instance, true) != OT_ERROR_NONE) { - printf("Failed to start Thread stack!\n"); - libtocksync_alarm_delay_ms(100); - } - - for ( ;;) { - otTaskletsProcess(instance); - otSysProcessDrivers(instance); - - if (send_local_temp) { - sendUdp(instance); - send_local_temp = false; - } - - if (!otTaskletsArePending(instance) && !openthread_platform_pending_work()) { - yield(); - } - - } - - return 0; -} - -void setNetworkConfiguration(otInstance* aInstance) { - otOperationalDataset aDataset; - - memset(&aDataset, 0, sizeof(otOperationalDataset)); - - /* Set Channel to 26 */ - aDataset.mChannel = 26; - aDataset.mComponents.mIsChannelPresent = true; - - /* Set Pan ID to abcd */ - aDataset.mPanId = (otPanId)0xabcd; - aDataset.mComponents.mIsPanIdPresent = true; - - /* Set network key to 00112233445566778899aabbccddeeff */ - uint8_t key[OT_NETWORK_KEY_SIZE] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; - memcpy(aDataset.mNetworkKey.m8, key, sizeof(aDataset.mNetworkKey)); - aDataset.mComponents.mIsNetworkKeyPresent = true; - - otError error = otDatasetSetActive(aInstance, &aDataset); - assert(error == 0); - -} - -static void stateChangeCallback(uint32_t flags, void* context) { - otInstance* instance = (otInstance*)context; - if (!(flags & OT_CHANGED_THREAD_ROLE)) { - return; - } - - switch (otThreadGetDeviceRole(instance)) { - case OT_DEVICE_ROLE_DISABLED: - printf("[State Change] - Disabled.\n"); - break; - case OT_DEVICE_ROLE_DETACHED: - printf("[State Change] - Detached.\n"); - break; - case OT_DEVICE_ROLE_CHILD: - network_up = true; - sendUdp(instance); - printf("[State Change] - Child.\n"); - printf("Successfully attached to Thread network as a child.\n"); - break; - case OT_DEVICE_ROLE_ROUTER: - printf("[State Change] - Router.\n"); - break; - case OT_DEVICE_ROLE_LEADER: - printf("[State Change] - Leader.\n"); - break; - default: - break; - } -} - -static void print_ip_addr(otInstance* instance) { - char addr_string[64]; - const otNetifAddress* unicastAddrs = otIp6GetUnicastAddresses(instance); - - printf("[THREAD] Device IPv6 Addresses: "); - for (const otNetifAddress* addr = unicastAddrs; addr; addr = addr->mNext) { - const otIp6Address ip6_addr = addr->mAddress; - otIp6AddressToString(&ip6_addr, addr_string, sizeof(addr_string)); - printf("%s\n", addr_string); - } -} - -void handleUdpReceive(void* aContext, otMessage* aMessage, - const otMessageInfo* aMessageInfo) { - OT_UNUSED_VARIABLE(aContext); - OT_UNUSED_VARIABLE(aMessageInfo); - char buf[2]; - - const otIp6Address sender_addr = aMessageInfo->mPeerAddr; - otIp6AddressToString(&sender_addr, buf, sizeof(buf)); - - otMessageRead(aMessage, otMessageGetOffset(aMessage), buf, sizeof(buf) - 1); - global_temperature_setpoint = buf[0]; -} - -void initUdp(otInstance* aInstance) { - otSockAddr listenSockAddr; - - memset(&sUdpSocket, 0, sizeof(sUdpSocket)); - memset(&listenSockAddr, 0, sizeof(listenSockAddr)); - - listenSockAddr.mPort = UDP_PORT; - - otUdpOpen(aInstance, &sUdpSocket, handleUdpReceive, aInstance); - otUdpBind(aInstance, &sUdpSocket, &listenSockAddr, OT_NETIF_THREAD); -} - -void sendUdp(otInstance* aInstance) { - - otError error = OT_ERROR_NONE; - otMessage* message; - otMessageInfo messageInfo; - otIp6Address destinationAddr; - - memset(&messageInfo, 0, sizeof(messageInfo)); - - otIp6AddressFromString(UDP_ROUTER_MULTICAST, &destinationAddr); - messageInfo.mPeerAddr = destinationAddr; - messageInfo.mPeerPort = UDP_PORT; - - message = otUdpNewMessage(aInstance, NULL); - if (message == NULL) { - printf("Error creating udp message\n"); - return; - } - - error = otMessageAppend(message, &local_temperature_setpoint, sizeof(local_temperature_setpoint)); - if (error != OT_ERROR_NONE && message != NULL) { - printf("Error appending to udp message\n"); - otMessageFree(message); - return; - } - - error = otUdpSend(aInstance, &sUdpSocket, message, &messageInfo); - if (error != OT_ERROR_NONE && message != NULL) { - printf("Error sending udp packet\n"); - otMessageFree(message); - } -} diff --git a/examples/tutorials/thread_network/screen/temperature_sensor_app/Makefile b/examples/tutorials/thread_network/07_screen_button/Makefile similarity index 82% rename from examples/tutorials/thread_network/screen/temperature_sensor_app/Makefile rename to examples/tutorials/thread_network/07_screen_button/Makefile index a55fe6dee..0748e329c 100644 --- a/examples/tutorials/thread_network/screen/temperature_sensor_app/Makefile +++ b/examples/tutorials/thread_network/07_screen_button/Makefile @@ -1,11 +1,13 @@ # Makefile for user application # Specify this directory relative to the current application. -TOCK_USERLAND_BASE_DIR = ../../../../.. +TOCK_USERLAND_BASE_DIR = ../../../../ # Which files to compile. C_SRCS := $(wildcard *.c) +PACKAGE_NAME = screen + # Include userland master makefile. Contains rules and flags for actually # building the application. include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tutorials/thread_network/07_screen_button/main.c b/examples/tutorials/thread_network/07_screen_button/main.c new file mode 100644 index 000000000..749af1eb6 --- /dev/null +++ b/examples/tutorials/thread_network/07_screen_button/main.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +#include + +static void button_callback(returncode_t ret, + int btn_num, + bool pressed) { + if (ret != RETURNCODE_SUCCESS) return; + + if (pressed) { + printf("Button %i pressed!\r\n", btn_num); + } +} + +int main(void) { + for (int i = 0; i < 4; i++) { + libtock_button_notify_on_press(i, button_callback); + } + + for ( ;;) { + yield(); + } +} diff --git a/examples/tutorials/thread_network/05_controller_final/Makefile b/examples/tutorials/thread_network/08_screen_u8g2/Makefile similarity index 88% rename from examples/tutorials/thread_network/05_controller_final/Makefile rename to examples/tutorials/thread_network/08_screen_u8g2/Makefile index bf65c96b6..2d56ef7d2 100644 --- a/examples/tutorials/thread_network/05_controller_final/Makefile +++ b/examples/tutorials/thread_network/08_screen_u8g2/Makefile @@ -6,9 +6,9 @@ TOCK_USERLAND_BASE_DIR = ../../../../ # Which files to compile. C_SRCS := $(wildcard *.c) -PACKAGE_NAME = thread_controller +PACKAGE_NAME = screen -STACK_SIZE = 4096 +STACK_SIZE = 4096 EXTERN_LIBS += $(TOCK_USERLAND_BASE_DIR)/u8g2 # Include userland master makefile. Contains rules and flags for actually diff --git a/examples/tutorials/thread_network/08_screen_u8g2/main.c b/examples/tutorials/thread_network/08_screen_u8g2/main.c new file mode 100644 index 000000000..f61203f42 --- /dev/null +++ b/examples/tutorials/thread_network/08_screen_u8g2/main.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +#include + +#include +#include + +// Global reference to the u8g2 context: +u8g2_t u8g2; + +// Helper method to update and format u8g2 screen. +static void update_screen(void); + +uint8_t global_temperature_setpoint = 0; +uint8_t local_temperature_setpoint = 22; +uint8_t measured_temperature = 0; + +static void button_callback(returncode_t ret, + int btn_num, + bool pressed) { + if (ret != RETURNCODE_SUCCESS) return; + + if (pressed) { + printf("Button %i pressed!\r\n", btn_num); + } +} + +int main(void) { + // Init u8g2 + u8g2_tock_init(&u8g2); + u8g2_SetFont(&u8g2, u8g2_font_profont12_tr); + u8g2_SetFontPosTop(&u8g2); + + for (int i = 0; i < 4; i++) { + libtock_button_notify_on_press(i, button_callback); + } + + update_screen(); + + for ( ;;) { + yield(); + } +} + +static void update_screen(void) { + char temperature_set_point_str[35]; + char temperature_global_set_point_str[35]; + char temperature_current_measure_str[35]; + + // Format the buffers to be written. + sprintf(temperature_set_point_str, + "Set Point: %d", + local_temperature_setpoint); + + sprintf(temperature_global_set_point_str, + "Global Set Point: %d", + global_temperature_setpoint); + + uint8_t whole_temp = measured_temperature / 100; + uint8_t decimal_temp = measured_temperature % 100; + + sprintf(temperature_current_measure_str, + "Measured Temp: %d.%d", + whole_temp, + decimal_temp); + + // Use u8g2 library to draw each string. + u8g2_ClearBuffer(&u8g2); + u8g2_SetDrawColor(&u8g2, 1); + u8g2_DrawStr(&u8g2, 0, 0, temperature_set_point_str); + u8g2_DrawStr(&u8g2, 0, 25, temperature_global_set_point_str); + u8g2_DrawStr(&u8g2, 0, 50, temperature_current_measure_str); + u8g2_SendBuffer(&u8g2); +} diff --git a/examples/tutorials/thread_network/04_controller_thread/Makefile b/examples/tutorials/thread_network/09_screen_final/Makefile similarity index 88% rename from examples/tutorials/thread_network/04_controller_thread/Makefile rename to examples/tutorials/thread_network/09_screen_final/Makefile index bf65c96b6..2d56ef7d2 100644 --- a/examples/tutorials/thread_network/04_controller_thread/Makefile +++ b/examples/tutorials/thread_network/09_screen_final/Makefile @@ -6,9 +6,9 @@ TOCK_USERLAND_BASE_DIR = ../../../../ # Which files to compile. C_SRCS := $(wildcard *.c) -PACKAGE_NAME = thread_controller +PACKAGE_NAME = screen -STACK_SIZE = 4096 +STACK_SIZE = 4096 EXTERN_LIBS += $(TOCK_USERLAND_BASE_DIR)/u8g2 # Include userland master makefile. Contains rules and flags for actually diff --git a/examples/tutorials/thread_network/09_screen_final/main.c b/examples/tutorials/thread_network/09_screen_final/main.c new file mode 100644 index 000000000..736211d99 --- /dev/null +++ b/examples/tutorials/thread_network/09_screen_final/main.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include + +#include + +#include +#include + +// Global reference to the u8g2 context. +u8g2_t u8g2; + +// Helper method to update and format u8g2 screen. +static void update_screen(void); + +uint8_t global_temperature_setpoint = 0; +uint8_t local_temperature_setpoint = 22; +uint8_t measured_temperature = 0; + +bool callback_event = false; + +static void button_callback(returncode_t ret, + int btn_num, + bool pressed) { + if (ret != RETURNCODE_SUCCESS) return; + + if (pressed) { + if (btn_num == 0 && local_temperature_setpoint < 35) { + local_temperature_setpoint++; + } else if (btn_num == 1 && local_temperature_setpoint > 0) { + local_temperature_setpoint--; + } else if (btn_num == 2) { + local_temperature_setpoint = 22; + } + } + + // Indicate that we have received a callback. + callback_event = true; +} + +int main(void) { + // Init u8g2 + u8g2_tock_init(&u8g2); + u8g2_SetFont(&u8g2, u8g2_font_profont12_tr); + u8g2_SetFontPosTop(&u8g2); + + for (int i = 0; i < 4; i++) { + libtock_button_notify_on_press(i, button_callback); + } + + for ( ;;) { + yield_for(&callback_event); + update_screen(); + } +} + +static void update_screen(void) { + char temperature_set_point_str[35]; + char temperature_global_set_point_str[35]; + char temperature_current_measure_str[35]; + + // Format the buffers to be written. + sprintf(temperature_set_point_str, + "Set Point: %d", + local_temperature_setpoint); + + sprintf(temperature_global_set_point_str, + "Global Set Point: %d", + global_temperature_setpoint); + + sprintf(temperature_current_measure_str, + "Measured Temp: %d", + measured_temperature); + + // Use u8g2 library to draw each string. + u8g2_ClearBuffer(&u8g2); + u8g2_SetDrawColor(&u8g2, 1); + u8g2_DrawStr(&u8g2, 0, 0, temperature_set_point_str); + u8g2_DrawStr(&u8g2, 0, 25, temperature_global_set_point_str); + u8g2_DrawStr(&u8g2, 0, 50, temperature_current_measure_str); + u8g2_SendBuffer(&u8g2); +} diff --git a/examples/tutorials/thread_network/10_screen_ipc/Makefile b/examples/tutorials/thread_network/10_screen_ipc/Makefile new file mode 100644 index 000000000..2d56ef7d2 --- /dev/null +++ b/examples/tutorials/thread_network/10_screen_ipc/Makefile @@ -0,0 +1,16 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../../ + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +PACKAGE_NAME = screen + +STACK_SIZE = 4096 +EXTERN_LIBS += $(TOCK_USERLAND_BASE_DIR)/u8g2 + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tutorials/thread_network/05_controller_final/main.c b/examples/tutorials/thread_network/10_screen_ipc/main.c similarity index 84% rename from examples/tutorials/thread_network/05_controller_final/main.c rename to examples/tutorials/thread_network/10_screen_ipc/main.c index ed129b012..c7d46e41f 100644 --- a/examples/tutorials/thread_network/05_controller_final/main.c +++ b/examples/tutorials/thread_network/10_screen_ipc/main.c @@ -3,46 +3,64 @@ #include #include +#include #include -#include #include #include -#include - #include #include +// Global reference to the u8g2 context. u8g2_t u8g2; -size_t sensor_svc_num = 0; -size_t openthread_svc_num = 0; - -uint8_t global_temperature_setpoint = 0; -uint8_t prior_global_temperature_setpoint = 255; +// Helper method to update and format u8g2 screen. +static void update_screen(void); -uint8_t local_temperature_setpoint = 22; -uint8_t prior_local_temperature_setpoint = 255; +uint8_t global_temperature_setpoint = 0; +uint8_t local_temperature_setpoint = 22; +int measured_temperature = 0; -int measured_temperature = 0; +uint8_t prior_global_temperature_setpoint = 255; +uint8_t prior_local_temperature_setpoint = 255; int prior_measured_temperature = 0; bool network_up = false; -// Callback event indicator bool callback_event = false; libtock_alarm_t read_temperature_timer; libtock_alarm_t network_timer; +// Helper method to initialize the screen. +static int init_controller_ipc(void); +size_t sensor_svc_num = 0; +size_t openthread_svc_num = 0; + // We use this variable as a buffer that is naturally aligned to the int // alignment, and has an alignment >= its size. uint8_t temperature_buffer[64] __attribute__((aligned(64))); uint8_t openthread_buffer[64] __attribute__((aligned(64))); -static void update_screen(void); -static int init_controller_ipc(void); +static void button_callback(returncode_t ret, + int btn_num, + bool pressed) { + if (ret != RETURNCODE_SUCCESS) return; + + if (pressed) { + if (btn_num == 0 && local_temperature_setpoint < 35) { + local_temperature_setpoint++; + } else if (btn_num == 1 && local_temperature_setpoint > 0) { + local_temperature_setpoint--; + } else if (btn_num == 2) { + local_temperature_setpoint = 22; + } + } + + // Indicate that we have received a callback. + callback_event = true; +} static void read_temperature_timer_callback(__attribute__ ((unused)) uint32_t now, __attribute__ ((unused)) uint32_t scheduled, @@ -51,7 +69,6 @@ static void read_temperature_timer_callback(__attribute__ ((unused)) uint32_t no ipc_notify_service(sensor_svc_num); } - static void update_network_timer_callback(__attribute__ ((unused)) uint32_t now, __attribute__ ((unused)) uint32_t scheduled, __attribute__ ((unused)) void* opaque) { @@ -89,31 +106,9 @@ static void openthread_callback(__attribute__ ((unused)) int pid, } -static void button_callback(returncode_t ret, - int btn_num, - bool pressed) { - if (ret != RETURNCODE_SUCCESS) return; - - if (pressed) { - if (btn_num == 0 && local_temperature_setpoint < 35) { - local_temperature_setpoint++; - } else if (btn_num == 1 && local_temperature_setpoint > 0) { - local_temperature_setpoint--; - } else if (btn_num == 2) { - local_temperature_setpoint = 22; - } - } - - // Indicate that we have received a callback. - callback_event = true; - - return; -} int main(void) { - int err; - int i; - + // Init u8g2 u8g2_tock_init(&u8g2); u8g2_SetFont(&u8g2, u8g2_font_profont12_tr); u8g2_SetFontPosTop(&u8g2); @@ -121,9 +116,8 @@ int main(void) { init_controller_ipc(); // Enable buttons - for (i = 0; i < 3; i++) { - err = libtock_button_notify_on_press(i, button_callback); - if (err < 0) return err; + for (int i = 0; i < 4; i++) { + libtock_button_notify_on_press(i, button_callback); } ipc_notify_service(sensor_svc_num); @@ -141,9 +135,46 @@ int main(void) { prior_local_temperature_setpoint = local_temperature_setpoint; update_screen(); } + } } +static void update_screen(void) { + char temperature_set_point_str[35]; + char temperature_global_set_point_str[35]; + char temperature_current_measure_str[35]; + + // Format the buffers to be written. + sprintf(temperature_set_point_str, + "Set Point: %d", + local_temperature_setpoint); + + if (network_up) { + sprintf(temperature_global_set_point_str, + "Global Set Point: %d", + global_temperature_setpoint); + } else { + sprintf(temperature_global_set_point_str, + "Global Set Point: N/A"); + } + + uint8_t whole_temp = measured_temperature / 100; + uint8_t decimal_temp = measured_temperature % 100; + + sprintf(temperature_current_measure_str, + "Measured Temp: %d.%d", + whole_temp, + decimal_temp); + + // Use u8g2 library to draw each string. + u8g2_ClearBuffer(&u8g2); + u8g2_SetDrawColor(&u8g2, 1); + u8g2_DrawStr(&u8g2, 0, 0, temperature_set_point_str); + u8g2_DrawStr(&u8g2, 0, 25, temperature_global_set_point_str); + u8g2_DrawStr(&u8g2, 0, 50, temperature_current_measure_str); + u8g2_SendBuffer(&u8g2); +} + static int init_controller_ipc(void) { int err = -1; int discover_retry_count = 0; @@ -180,27 +211,3 @@ static int init_controller_ipc(void) { return err; } - -static void update_screen(void) { - char temperature_set_point_str[35]; - char temperature_global_set_point_str[35]; - char temperature_current_measure_str[35]; - sprintf(temperature_set_point_str, "Set Point: %d", local_temperature_setpoint); - - if (network_up) { - sprintf(temperature_global_set_point_str, "Global Set Point: %d", global_temperature_setpoint); - } else { - sprintf(temperature_global_set_point_str, "Global Set Point: N/A"); - } - - // print measured temperature as value XX.25 - sprintf(temperature_current_measure_str, "Measured Temp: %d.%02d", measured_temperature / 100, - measured_temperature % 100); - - u8g2_ClearBuffer(&u8g2); - u8g2_SetDrawColor(&u8g2, 1); - u8g2_DrawStr(&u8g2, 0, 0, temperature_set_point_str); - u8g2_DrawStr(&u8g2, 0, 25, temperature_global_set_point_str); - u8g2_DrawStr(&u8g2, 0, 50, temperature_current_measure_str); - u8g2_SendBuffer(&u8g2); -} diff --git a/examples/tutorials/thread_network/11_sensor_ipc/Makefile b/examples/tutorials/thread_network/11_sensor_ipc/Makefile new file mode 100644 index 000000000..2a2f63413 --- /dev/null +++ b/examples/tutorials/thread_network/11_sensor_ipc/Makefile @@ -0,0 +1,13 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../../ + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +PACKAGE_NAME = org.tockos.thread-tutorial.sensor + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tutorials/thread_network/screen/temperature_sensor_app/main.c b/examples/tutorials/thread_network/11_sensor_ipc/main.c similarity index 72% rename from examples/tutorials/thread_network/screen/temperature_sensor_app/main.c rename to examples/tutorials/thread_network/11_sensor_ipc/main.c index 3a92056a7..f9dd4f2e8 100644 --- a/examples/tutorials/thread_network/screen/temperature_sensor_app/main.c +++ b/examples/tutorials/thread_network/11_sensor_ipc/main.c @@ -5,10 +5,6 @@ #include #include -// Global variable storing the current temperature. This is written to in the -// main loop, and read from in the IPC handler. Because the app is single -// threaded and has no yield point when writing the value, we do not need to -// worry about synchronization -- reads never happen during a write. static int current_temperature = 0; static void sensor_ipc_callback(int pid, int len, int buf, @@ -28,6 +24,7 @@ static void sensor_ipc_callback(int pid, int len, int buf, ipc_notify_client(pid); } + int main(void) { // Measure the temperature once before registering ourselves as an IPC // service. This ensures that we always return a correct (but potentially @@ -39,12 +36,13 @@ int main(void) { sensor_ipc_callback, NULL); - // We measure the temperature in the main loop and simply provide the latest - // reading in an IPC. This means that the control app does not have to wait - // for the temperature read system call to complete. + // We measure the temperature in the main loop and + // print this value to the console. while (1) { + // Measure temperature -- returned in the form 2200 => 22C libtocksync_temperature_read(¤t_temperature); - // printf("Current temperature: %d\r\n", current_temperature); + + // Delay 1000 ms (1 second). libtocksync_alarm_delay_ms(1000); } } diff --git a/examples/tutorials/thread_network/12_openthread_ipc/Makefile b/examples/tutorials/thread_network/12_openthread_ipc/Makefile new file mode 100644 index 000000000..5c487ec99 --- /dev/null +++ b/examples/tutorials/thread_network/12_openthread_ipc/Makefile @@ -0,0 +1,20 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Specify this app depends on the MTD OpenThread library. +include $(TOCK_USERLAND_BASE_DIR)/libopenthread/libopenthread-mtd.mk + +# set stack size to 8000 to support openthread app +STACK_SIZE:=8000 +APP_HEAP_SIZE:=5000 + +PACKAGE_NAME = org.tockos.thread-tutorial.openthread + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tutorials/thread_network/screen/openthread_app/main.c b/examples/tutorials/thread_network/12_openthread_ipc/main.c similarity index 76% rename from examples/tutorials/thread_network/screen/openthread_app/main.c rename to examples/tutorials/thread_network/12_openthread_ipc/main.c index 82ec813a9..7eed4ff55 100644 --- a/examples/tutorials/thread_network/screen/openthread_app/main.c +++ b/examples/tutorials/thread_network/12_openthread_ipc/main.c @@ -18,12 +18,23 @@ #include #include -#define UDP_PORT 1212 -static const char UDP_ROUTER_MULTICAST[] = "ff02::2"; +// helper utility demonstrating network config setup +static void setNetworkConfiguration(otInstance* aInstance); static otUdpSocket sUdpSocket; -static void initUdp(otInstance* aInstance); -static void sendUdp(otInstance* aInstance); + +void initUdp(otInstance* aInstance); + +void handleUdpRecvTemperature(void* aContext, otMessage* aMessage, + const otMessageInfo* aMessageInfo); + +void sendUdpTemperature(otInstance* aInstance, uint8_t temperature); + +// callback for Thread state change events +static void stateChangeCallback(uint32_t flags, void* context); + +// helper utility to print ip address +static void print_ip_addr(otInstance* instance); uint8_t local_temperature_setpoint = 22; uint8_t global_temperature_setpoint = 255; @@ -31,10 +42,6 @@ uint8_t prior_global_temperature_setpoint = 255; bool network_up = false; bool send_local_temp = false; -// Callback method for received udp packets. -static void handleUdpReceive(void* aContext, otMessage* aMessage, - const otMessageInfo* aMessageInfo); - static void openthread_ipc_callback(int pid, int len, int buf, __attribute__((unused)) void* ud) { // A client has requested us to provide them the current temperature value. @@ -66,15 +73,6 @@ static void openthread_ipc_callback(int pid, int len, int buf, } } -// helper utility demonstrating network config setup -static void setNetworkConfiguration(otInstance* aInstance); - -// callback for Thread state change events -static void stateChangeCallback(uint32_t flags, void* context); - -// helper utility to print ip address -static void print_ip_addr(otInstance* instance); - int main(__attribute__((unused)) int argc, __attribute__((unused)) char* argv[]) { // Initialize OpenThread instance. otSysInit(argc, argv); @@ -87,49 +85,58 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char* argv[]) openthread_ipc_callback, NULL); - /* As part of the initialization, we will: - - Init dataset with the following properties: - - channel: 26 - - network key: 0x00112233445566778899aabbccddeeff - - PAN ID: 0xabcd - - configure ip addr (ifconfig up) - - start thread network (thread start) - */ + // set child timeout to 60 seconds. + otThreadSetChildTimeout(instance, 60); + // Set callback to be notified when thread state changes. + otSetStateChangedCallback(instance, stateChangeCallback, instance); + + /////////////////////////////////////////////////// + // THREAD NETWORK SETUP HERE + + // Configure network. setNetworkConfiguration(instance); - // set child timeout to 60 seconds - otThreadSetChildTimeout(instance, 60); + // Init UDP interface. + initUdp(instance); - /* Start the Thread network interface (CLI cmd -> ifconfig up) */ + // Enable network interface. while (otIp6SetEnabled(instance, true) != OT_ERROR_NONE) { printf("Failed to start Thread network interface!\n"); libtocksync_alarm_delay_ms(100); } - otSetStateChangedCallback(instance, stateChangeCallback, instance); - + // Print IPv6 address. print_ip_addr(instance); - // Initialize UDP socket (see guide: https://openthread.io/codelabs/openthread-apis#7) - initUdp(instance); - - /* Start the Thread stack (CLI cmd -> thread start) */ + // Start Thread network. while (otThreadSetEnabled(instance, true) != OT_ERROR_NONE) { printf("Failed to start Thread stack!\n"); libtocksync_alarm_delay_ms(100); } + // + //////////////////////////////////////////////////// + + // OpenThread main loop. for ( ;;) { + // Execute any pending OpenThread related work. otTaskletsProcess(instance); + + // Execute any platform related work (e.g. check + // radio buffer for new packets). otSysProcessDrivers(instance); + // Send udp packet if pending temperature update. if (send_local_temp) { - sendUdp(instance); + sendUdpTemperature(instance, local_temperature_setpoint); send_local_temp = false; } - if (!otTaskletsArePending(instance) && !openthread_platform_pending_work()) { + // If there is not pending platform or OpenThread + // related work -- yield. + if (!otTaskletsArePending(instance) && + !openthread_platform_pending_work()) { yield(); } @@ -138,6 +145,12 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char* argv[]) return 0; } +// Helper method that configures the OpenThread network dataset +// for the desired tutorial configuration. +// We set the following dataset parameters: +// -- Channel: 26 +// -- PanId: 0xabcd +// -- Networkkey: 00112233445566778899aabbccddeeff void setNetworkConfiguration(otInstance* aInstance) { otOperationalDataset aDataset; @@ -162,6 +175,8 @@ void setNetworkConfiguration(otInstance* aInstance) { } +// Helper method that registers a stateChangeCallback to print +// when state changes occur (useful for debugging). static void stateChangeCallback(uint32_t flags, void* context) { otInstance* instance = (otInstance*)context; if (!(flags & OT_CHANGED_THREAD_ROLE)) { @@ -176,10 +191,10 @@ static void stateChangeCallback(uint32_t flags, void* context) { printf("[State Change] - Detached.\n"); break; case OT_DEVICE_ROLE_CHILD: - network_up = true; - sendUdp(instance); printf("[State Change] - Child.\n"); printf("Successfully attached to Thread network as a child.\n"); + sendUdpTemperature(instance, local_temperature_setpoint); + network_up = true; break; case OT_DEVICE_ROLE_ROUTER: printf("[State Change] - Router.\n"); @@ -192,6 +207,8 @@ static void stateChangeCallback(uint32_t flags, void* context) { } } +// Helper method to print the given Thread node's registered +// ipv6 address. static void print_ip_addr(otInstance* instance) { char addr_string[64]; const otNetifAddress* unicastAddrs = otIp6GetUnicastAddresses(instance); @@ -204,8 +221,9 @@ static void print_ip_addr(otInstance* instance) { } } -void handleUdpReceive(void* aContext, otMessage* aMessage, - const otMessageInfo* aMessageInfo) { + +void handleUdpRecvTemperature(void* aContext, otMessage* aMessage, + const otMessageInfo* aMessageInfo) { OT_UNUSED_VARIABLE(aContext); OT_UNUSED_VARIABLE(aMessageInfo); char buf[2]; @@ -223,13 +241,13 @@ void initUdp(otInstance* aInstance) { memset(&sUdpSocket, 0, sizeof(sUdpSocket)); memset(&listenSockAddr, 0, sizeof(listenSockAddr)); - listenSockAddr.mPort = UDP_PORT; + listenSockAddr.mPort = 1212; - otUdpOpen(aInstance, &sUdpSocket, handleUdpReceive, aInstance); + otUdpOpen(aInstance, &sUdpSocket, handleUdpRecvTemperature, aInstance); otUdpBind(aInstance, &sUdpSocket, &listenSockAddr, OT_NETIF_THREAD); } -void sendUdp(otInstance* aInstance) { +void sendUdpTemperature(otInstance* aInstance, uint8_t temperature) { otError error = OT_ERROR_NONE; otMessage* message; @@ -238,9 +256,9 @@ void sendUdp(otInstance* aInstance) { memset(&messageInfo, 0, sizeof(messageInfo)); - otIp6AddressFromString(UDP_ROUTER_MULTICAST, &destinationAddr); + otIp6AddressFromString("ff02::02", &destinationAddr); messageInfo.mPeerAddr = destinationAddr; - messageInfo.mPeerPort = UDP_PORT; + messageInfo.mPeerPort = 1212; message = otUdpNewMessage(aInstance, NULL); if (message == NULL) { @@ -248,7 +266,7 @@ void sendUdp(otInstance* aInstance) { return; } - error = otMessageAppend(message, &local_temperature_setpoint, sizeof(local_temperature_setpoint)); + error = otMessageAppend(message, &temperature, 1); if (error != OT_ERROR_NONE && message != NULL) { printf("Error appending to udp message\n"); otMessageFree(message); diff --git a/examples/tutorials/thread_network/screen/temperature_controller/Makefile b/examples/tutorials/thread_network/screen/temperature_controller/Makefile deleted file mode 100644 index 2563ad044..000000000 --- a/examples/tutorials/thread_network/screen/temperature_controller/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# Makefile for user application - -# Specify this directory relative to the current application. -TOCK_USERLAND_BASE_DIR = ../../../../.. - -# Which files to compile. -C_SRCS := $(wildcard *.c) - -PACKAGE_NAME = thread_controller - -STACK_SIZE = 4096 -EXTERN_LIBS += $(TOCK_USERLAND_BASE_DIR)/u8g2 - -# Include userland master makefile. Contains rules and flags for actually -# building the application. -include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk \ No newline at end of file diff --git a/examples/tutorials/thread_network/screen/temperature_controller/main.c b/examples/tutorials/thread_network/screen/temperature_controller/main.c deleted file mode 100644 index b70c92d47..000000000 --- a/examples/tutorials/thread_network/screen/temperature_controller/main.c +++ /dev/null @@ -1,208 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -u8g2_t u8g2; - -size_t sensor_svc_num = 0; -size_t openthread_svc_num = 0; - -uint8_t global_temperature_setpoint = 0; -uint8_t prior_global_temperature_setpoint = 255; - -uint8_t local_temperature_setpoint = 22; -uint8_t prior_local_temperature_setpoint = 255; - -int measured_temperature = 0; -int prior_measured_temperature = 0; - -bool network_up = false; - -// Callback event indicator -bool callback_event = false; - -libtock_alarm_t read_temperature_timer; -libtock_alarm_t network_timer; - -// We use this variable as a buffer that is naturally aligned to the int -// alignment, and has an alignment >= its size. -uint8_t temperature_buffer[64] __attribute__((aligned(64))); -uint8_t openthread_buffer[64] __attribute__((aligned(64))); - -static void update_screen(void); -static int init_controller_ipc(void); - - -static void read_temperature_timer_callback(__attribute__ ((unused)) uint32_t now, - __attribute__ ((unused)) uint32_t scheduled, - __attribute__ ((unused)) void* opaque) { - // Request a new temperature reading from the sensor: - ipc_notify_service(sensor_svc_num); -} - - -static void update_network_timer_callback(__attribute__ ((unused)) uint32_t now, - __attribute__ ((unused)) uint32_t scheduled, - __attribute__ ((unused)) void* opaque) { - openthread_buffer[0] = local_temperature_setpoint; - ipc_notify_service(openthread_svc_num); - libtock_alarm_in_ms(250, update_network_timer_callback, NULL, &network_timer); - -} - -static void sensor_callback(__attribute__ ((unused)) int pid, - __attribute__ ((unused)) int len, - __attribute__ ((unused)) int arg2, - __attribute__ ((unused)) void* ud) { - // update measured temperature - measured_temperature = *((int*) &temperature_buffer[0]); - - // Indicate that we have received a callback. - callback_event = true; - - // Request a new temperature reading in 250ms: - libtock_alarm_in_ms(250, read_temperature_timer_callback, NULL, &read_temperature_timer); -} - -static void openthread_callback(__attribute__ ((unused)) int pid, - __attribute__ ((unused)) int len, - __attribute__ ((unused)) int arg2, - __attribute__ ((unused)) void* ud) { - network_up = true; - - // update setpoint temperature - global_temperature_setpoint = *((int*) &openthread_buffer[0]); - - // Indicate that we have received a callback. - callback_event = true; - -} - -static void button_callback(returncode_t ret, - int btn_num, - bool pressed) { - if (ret != RETURNCODE_SUCCESS) return; - - if (pressed) { - if (btn_num == 0 && local_temperature_setpoint < 35) { - local_temperature_setpoint++; - } else if (btn_num == 1 && local_temperature_setpoint > 0) { - local_temperature_setpoint--; - } else if (btn_num == 2) { - local_temperature_setpoint = 22; - } - } - - // Indicate that we have received a callback. - callback_event = true; - - return; -} - -int main(void) { - int err; - int i; - - u8g2_tock_init(&u8g2); - u8g2_SetFont(&u8g2, u8g2_font_profont12_tr); - u8g2_SetFontPosTop(&u8g2); - - init_controller_ipc(); - - // Enable buttons - for (i = 0; i < 3; i++) { - err = libtock_button_notify_on_press(i, button_callback); - if (err < 0) return err; - } - - ipc_notify_service(sensor_svc_num); - libtock_alarm_in_ms(500, update_network_timer_callback, NULL, &network_timer); - - for ( ;;) { - callback_event = false; - yield_for(&callback_event); - - if (measured_temperature != prior_measured_temperature - || global_temperature_setpoint != prior_global_temperature_setpoint - || local_temperature_setpoint != prior_local_temperature_setpoint) { - prior_measured_temperature = measured_temperature; - prior_global_temperature_setpoint = global_temperature_setpoint; - prior_local_temperature_setpoint = local_temperature_setpoint; - update_screen(); - } - } -} - -static int init_controller_ipc(void) { - int err = -1; - int discover_retry_count = 0; - int err_sensor = -1; - int err_openthread = -1; - - while (err_sensor < 0 && err_openthread < 0 && discover_retry_count < 100) { - err_sensor = ipc_discover("org.tockos.thread-tutorial.sensor", &sensor_svc_num); - err_openthread = ipc_discover("org.tockos.thread-tutorial.openthread", &openthread_svc_num); - discover_retry_count++; - if (err < 0) { - libtocksync_alarm_delay_ms(10); - } - } - - if (err_sensor < 0) { - printf("No sensor service\r\n"); - return -1; - } - - if (err_openthread < 0) { - printf("No openthread service\r\n"); - return -1; - } - - printf("[controller] Discovered sensor service: %d\r\n", sensor_svc_num); - printf("[controller] Discovered openthread service: %d\r\n", openthread_svc_num); - - ipc_register_client_callback(sensor_svc_num, sensor_callback, NULL); - ipc_register_client_callback(openthread_svc_num, openthread_callback, NULL); - - // rb->length = snprintf(rb->buf, sizeof(rb->buf), "Hello World!"); - ipc_share(sensor_svc_num, &temperature_buffer, sizeof(temperature_buffer)); - - ipc_share(openthread_svc_num, &openthread_buffer, sizeof(openthread_buffer)); - - return err; -} - -static void update_screen(void) { - char temperature_set_point_str[35]; - char temperature_global_set_point_str[35]; - char temperature_current_measure_str[35]; - sprintf(temperature_set_point_str, "Set Point: %d", local_temperature_setpoint); - - if (network_up) { - sprintf(temperature_global_set_point_str, "Global Set Point: %d", global_temperature_setpoint); - } else { - sprintf(temperature_global_set_point_str, "Global Set Point: N/A"); - } - - // print measured temperature as value XX.25 - sprintf(temperature_current_measure_str, "Measured Temp: %d.%02d", measured_temperature / 100, - measured_temperature % 100); - - u8g2_ClearBuffer(&u8g2); - u8g2_SetDrawColor(&u8g2, 1); - u8g2_DrawStr(&u8g2, 0, 0, temperature_set_point_str); - u8g2_DrawStr(&u8g2, 0, 25, temperature_global_set_point_str); - u8g2_DrawStr(&u8g2, 0, 50, temperature_current_measure_str); - u8g2_SendBuffer(&u8g2); -}