Changeset - 33512a8a338c
activate.sh
Show inline comments
 
new file 100755
 
. ~/Projects/esp-idf-test/esp-idf/export.sh
components/esp-osc/.github/workflows/test.yml
Show inline comments
 
new file 100644
 
on: [push, pull_request]
 
name: Test
 
jobs:
 
  test:
 
    runs-on: ubuntu-latest
 
    steps:
 
      - name: Checkout
 
        uses: actions/checkout@v2
 
        with:
 
          submodules: recursive
 
      - name: Prepare
 
        run: sudo apt-get install python-is-python3 gperf -y
 
      - name: Install
 
        run: make prepare install
 
      - name: Build
 
        run: make build
components/esp-osc/.gitignore
Show inline comments
 
new file 100644
 
.idea/
 
cmake-build-debug/
 
test/esp-idf
 
test/tools
 
test/build
components/esp-osc/CMakeLists.txt
Show inline comments
 
new file 100644
 
set(srcs
 
    "esp_osc.c"
 
    "tinyosc.c"
 
)
 

	
 
idf_component_register(SRCS ${srcs}
 
    INCLUDE_DIRS "."
 
)
components/esp-osc/Kconfig
Show inline comments
 
new file 100644
components/esp-osc/LICENSE.md
Show inline comments
 
new file 100644
 
MIT License
 

	
 
Copyright (c) 2022 Joël Gähwiler
 

	
 
Permission is hereby granted, free of charge, to any person obtaining a copy
 
of this software and associated documentation files (the "Software"), to deal
 
in the Software without restriction, including without limitation the rights
 
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
copies of the Software, and to permit persons to whom the Software is
 
furnished to do so, subject to the following conditions:
 

	
 
The above copyright notice and this permission notice shall be included in all
 
copies or substantial portions of the Software.
 

	
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
SOFTWARE.
components/esp-osc/Makefile
Show inline comments
 
new file 100644
 
SHELL := /bin/bash
 

	
 
ESP_IDF_VERSION := "v5.2.2"
 

	
 
fmt:
 
	clang-format -i ./*.c ./*.h -style="{BasedOnStyle: Google, ColumnLimit: 120, SortIncludes: false}"
 
	clang-format -i ./test/main/*.c -style="{BasedOnStyle: Google, ColumnLimit: 120, SortIncludes: false}"
 

	
 
prepare:
 
	git clone --recursive  https://github.com/espressif/esp-idf.git test/esp-idf
 
	cd test/esp-idf; git fetch; git checkout $(ESP_IDF_VERSION)
 
	cd test/esp-idf/; git submodule update --recursive --init
 

	
 
update:
 
	cd test/esp-idf; git fetch; git checkout $(ESP_IDF_VERSION)
 
	cd test/esp-idf/; git submodule update --recursive --init
 

	
 
install:
 
	export IDF_TOOLS_PATH=$(shell pwd)/test/tools; cd test/esp-idf; ./install.sh esp32
 

	
 
config:
 
	export IDF_TOOLS_PATH=$(shell pwd)/test/tools; . test/esp-idf/export.sh; cd test; idf.py menuconfig
 

	
 
reconfigure:
 
	export IDF_TOOLS_PATH=$(shell pwd)/test/tools; . test/esp-idf/export.sh; cd test; idf.py reconfigure
 
	export IDF_TOOLS_PATH=$(shell pwd)/test/tools; . test/esp-idf/export.sh; cd test; idf.py fullclean
 

	
 
erase:
 
	export IDF_TOOLS_PATH=$(shell pwd)/test/tools; . test/esp-idf/export.sh; cd test; idf.py erase-flash
 

	
 
clean:
 
	export IDF_TOOLS_PATH=$(shell pwd)/test/tools; . test/esp-idf/export.sh; cd test; idf.py clean
 

	
 
build:
 
	export IDF_TOOLS_PATH=$(shell pwd)/test/tools; . test/esp-idf/export.sh; cd test; idf.py build
 

	
 
flash:
 
	export IDF_TOOLS_PATH=$(shell pwd)/test/tools; . test/esp-idf/export.sh;  cd test; idf.py flash
 

	
 
monitor:
 
	export IDF_TOOLS_PATH=$(shell pwd)/test/tools; . test/esp-idf/export.sh;  cd test; idf.py monitor
 

	
 
simple-monitor:
 
	@clear
 
	miniterm.py /dev/cu.SLAB_USBtoUART 115200 --rts 0 --dtr 0 --raw --exit-char 99
 

	
 
run: build flash monitor
components/esp-osc/README.md
Show inline comments
 
new file 100644
 
# esp-osc
 

	
 
[![Test](https://github.com/256dpi/esp-osc/actions/workflows/test.yml/badge.svg)](https://github.com/256dpi/esp-osc/actions/workflows/test.yml)
 
[![Release](https://img.shields.io/github/release/256dpi/esp-osc.svg)](https://github.com/256dpi/esp-osc/releases)
 

	
 
**OSC component for esp-idf projects based on the [tinyosc](https://github.com/mhroth/tinyosc) library**
 

	
 
This component bundles the tinyosc library and provides a simple API for sending OSC messages.
 

	
 
## Installation
 

	
 
You can install the component by adding it as a git submodule:
 

	
 
```bash
 
git submodule add https://github.com/256dpi/esp-osc.git components/esp-osc
 
git submodule update --init --recursive
 
```
 

	
 
## Example
 

	
 
An example can be found here: https://github.com/256dpi/esp-osc/blob/master/test/main/main.c.
components/esp-osc/esp_osc.c
Show inline comments
 
new file 100644
 
#include <stdio.h>
 
#include <stdarg.h>
 
#include <esp_log.h>
 

	
 
#include "esp_osc.h"
 

	
 
#define TAG "esp-osc"
 

	
 
bool esp_osc_init(esp_osc_client_t *client, uint16_t buf_len, uint16_t port) {
 
  // free existing memory
 
  if (client->sbuf != NULL) {
 
    free(client->sbuf);
 
  }
 
  if (client->rbuf != NULL) {
 
    free(client->rbuf);
 
  }
 

	
 
  // allocate memory
 
  client->sbuf = malloc(buf_len);
 
  client->rbuf = malloc(buf_len);
 
  client->len = buf_len;
 

	
 
  // close existing socket
 
  if (client->socket != 0) {
 
    close(client->socket);
 
    client->socket = 0;
 
  }
 

	
 
  // create socket
 
  client->socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
 
  if (client->socket <= 0) {
 
    ESP_LOGE(TAG, "failed to create socket (%d)", errno);
 
    client->socket = 0;
 
    return false;
 
  }
 

	
 
  // bind socket if port is available
 
  if (port > 0) {
 
    struct sockaddr_in addr = {0};
 
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
 
    addr.sin_family = AF_INET;
 
    addr.sin_port = htons(port);
 
    if (bind(client->socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
 
      ESP_LOGE(TAG, "failed to bind socket (%d)", errno);
 
      return false;
 
    }
 
  }
 

	
 
  return true;
 
}
 

	
 
esp_osc_target_t esp_osc_target(const char *address, uint16_t port) {
 
  // prepare target
 
  esp_osc_target_t target = {0};
 
  target.addr.sin_addr.s_addr = inet_addr(address);
 
  target.addr.sin_family = AF_INET;
 
  target.addr.sin_port = htons(port);
 

	
 
  return target;
 
}
 

	
 
bool esp_osc_send(esp_osc_client_t *client, esp_osc_target_t *target, const char *topic, const char *format, ...) {
 
  // send message
 
  va_list args;
 
  va_start(args, format);
 
  bool ret = esp_osc_send_v(client, target, topic, format, args);
 
  va_end(args);
 

	
 
  return ret;
 
}
 

	
 
bool esp_osc_send_v(esp_osc_client_t *client, esp_osc_target_t *target, const char *topic, const char *format,
 
                    va_list args) {
 
  // prepare message
 
  uint32_t length = tosc_vwrite((char *)client->sbuf, client->len, topic, format, args);
 

	
 
  // send message
 
  if (sendto(client->socket, client->sbuf, length, 0, (struct sockaddr *)&target->addr, sizeof(target->addr)) < 0) {
 
    ESP_LOGE(TAG, "failed to send message (%d)", errno);
 
    return false;
 
  }
 

	
 
  return true;
 
}
 

	
 
bool esp_osc_receive(esp_osc_client_t *client, esp_osc_callback_t callback) {
 
  // prepare values
 
  esp_osc_value_t values[32];
 

	
 
  for (;;) {
 
    // receive message
 
    ssize_t ret = recvfrom(client->socket, client->rbuf, client->len, 0, NULL, NULL);
 
    if (ret < 0) {
 
      ESP_LOGE(TAG, "failed to receive message (%d)", errno);
 
      return false;
 
    } else if (ret > client->len) {
 
      ESP_LOGE(TAG, "discard too long message (%d)", ret);
 
      return false;
 
    }
 

	
 
    // check bundle
 
    if (tosc_isBundle(client->rbuf)) {
 
      ESP_LOGE(TAG, "discard unsupported bundle");
 
      return false;
 
    }
 

	
 
    // parse message
 
    tosc_message msg = {0};
 
    int res = tosc_parseMessage(&msg, client->rbuf, client->len);
 
    if (res < 0) {
 
      ESP_LOGE(TAG, "failed to parse message (%d)", res);
 
      return false;
 
    }
 

	
 
    // get format
 
    char *fmt = tosc_getFormat(&msg);
 

	
 
    // get size
 
    size_t size = strlen(fmt);
 
    if (size > sizeof(values)) {
 
      ESP_LOGE(TAG, "message has too many values (%d)", size);
 
      return false;
 
    }
 

	
 
    // parse values
 
    for (size_t i = 0; i < size; i++) {
 
      switch (fmt[i]) {
 
        case 'i':
 
          values[i].i = tosc_getNextInt32(&msg);
 
          break;
 
        case 'h':
 
          values[i].h = tosc_getNextInt64(&msg);
 
          break;
 
        case 'f':
 
          values[i].f = tosc_getNextFloat(&msg);
 
          break;
 
        case 'd':
 
          values[i].d = tosc_getNextDouble(&msg);
 
          break;
 
        case 's':
 
          values[i].s = tosc_getNextString(&msg);
 
          break;
 
        case 'b':
 
          tosc_getNextBlob(&msg, &values[i].b, &values[i].bl);
 
          break;
 
      }
 
    }
 

	
 
    // call callback
 
    if (!callback(tosc_getAddress(&msg), fmt, values)) {
 
      return false;
 
    }
 
  }
 

	
 
  return true;
 
}
components/esp-osc/esp_osc.h
Show inline comments
 
new file 100644
 
#ifndef ESP_OSC_H
 
#define ESP_OSC_H
 

	
 
#include <stdbool.h>
 
#include <stdint.h>
 
#include <lwip/netdb.h>
 
#include <tinyosc.h>
 

	
 
#ifdef __cplusplus
 
extern "C" {
 
#endif
 

	
 
typedef struct {
 
  char *sbuf;
 
  char *rbuf;
 
  size_t len;
 
  int socket;
 
} esp_osc_client_t;
 

	
 
typedef struct {
 
  struct sockaddr_in addr;
 
} esp_osc_target_t;
 

	
 
typedef struct {
 
  union {
 
    int32_t i;
 
    int64_t h;
 
    float f;
 
    double d;
 
    const char *s;
 
    const char *b;
 
  };
 
  int bl;
 
} esp_osc_value_t;
 

	
 
typedef bool (*esp_osc_callback_t)(const char *topic, const char *format, esp_osc_value_t *values);
 

	
 
bool esp_osc_init(esp_osc_client_t *client, uint16_t buf_len, uint16_t port);
 

	
 
esp_osc_target_t esp_osc_target(const char *address, uint16_t port);
 

	
 
bool esp_osc_send(esp_osc_client_t *client, esp_osc_target_t *target, const char *topic, const char *format, ...);
 
bool esp_osc_send_v(esp_osc_client_t *client, esp_osc_target_t *target, const char *topic, const char *format,
 
                    va_list args);
 

	
 
bool esp_osc_receive(esp_osc_client_t *client, esp_osc_callback_t callback);
 

	
 
#ifdef __cplusplus
 
}
 
#endif
 

	
 
#endif  // ESP_OSC_H
components/esp-osc/test/CMakeLists.txt
Show inline comments
 
new file 100644
 
cmake_minimum_required(VERSION 3.5)
 
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
 
project(esp-osc)
components/esp-osc/test/components/esp-osc
Show inline comments
 
new file 120000
 
../../
 
\ No newline at end of file
components/esp-osc/test/main/CMakeLists.txt
Show inline comments
 
new file 100644
 
idf_component_register(SRCS "main.c" INCLUDE_DIRS "")
components/esp-osc/test/main/main.c
Show inline comments
 
new file 100644
 
#include <stdlib.h>
 

	
 
#include <esp_event.h>
 
#include <esp_log.h>
 
#include <esp_wifi.h>
 
#include <nvs_flash.h>
 

	
 
#include <esp_osc.h>
 

	
 
#define WIFI_SSID ""
 
#define WIFI_PASS ""
 

	
 
#define OSC_ADDRESS ""
 
#define OSC_PORT 0
 

	
 
#define TAG "main"
 

	
 
esp_osc_client_t client;
 

	
 
static void sender() {
 
  // select targets
 
  esp_osc_target_t targets[2] = {
 
      esp_osc_target("127.0.0.1", 9000),
 
      esp_osc_target(OSC_ADDRESS, OSC_PORT),
 
  };
 

	
 
  for (;;) {
 
    // delay
 
    vTaskDelay(1000 / portTICK_PERIOD_MS);
 

	
 
    // send messages
 
    for (size_t i = 0; i < 2; i++) {
 
      esp_osc_send(&client, &targets[i], "test", "ihfdsb", 42, (int64_t)84, 3.14f, 6.28, "foo", 3, "bar");
 
    }
 
  }
 
}
 

	
 
static bool callback(const char *topic, const char *format, esp_osc_value_t *values) {
 
  // log message
 
  ESP_LOGI(TAG, "got message: %s (%s)", topic, format);
 
  for (size_t i = 0; i < strlen(format); i++) {
 
    switch (format[i]) {
 
      case 'i':
 
        ESP_LOGI(TAG, "==> i: %ld", values[i].i);
 
        break;
 
      case 'h':
 
        ESP_LOGI(TAG, "==> h: %lld", values[i].h);
 
        break;
 
      case 'f':
 
        ESP_LOGI(TAG, "==> f: %f", values[i].f);
 
        break;
 
      case 'd':
 
        ESP_LOGI(TAG, "==> d: %f", values[i].d);
 
        break;
 
      case 's':
 
        ESP_LOGI(TAG, "==> s: %s", values[i].s);
 
        break;
 
      case 'b':
 
        ESP_LOGI(TAG, "==> b: %.*s (%d)", values[i].bl, values[i].b, values[i].bl);
 
        break;
 
    }
 
  }
 

	
 
  return true;
 
}
 

	
 
static void receiver() {
 
  for (;;) {
 
    // receive messages
 
    esp_osc_receive(&client, callback);
 
  }
 
}
 

	
 
static void restarter() {
 
  for (;;) {
 
    // delay
 
    vTaskDelay(5000 / portTICK_PERIOD_MS);
 

	
 
    // restart client
 
    esp_osc_init(&client, 1024, 9000);
 
  }
 
}
 

	
 
static void handler(void *arg, esp_event_base_t base, int32_t id, void *data) {
 
  if (base == WIFI_EVENT) {
 
    switch (id) {
 
      case WIFI_EVENT_STA_START:
 
        // connect to ap
 
        ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_connect());
 

	
 
        break;
 

	
 
      case IP_EVENT_STA_GOT_IP:
 
        break;
 

	
 
      case WIFI_EVENT_STA_DISCONNECTED:
 
        // reconnect Wi-Fi
 
        ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_connect());
 

	
 
        break;
 

	
 
      default:
 
        break;
 
    }
 
  }
 
}
 

	
 
void app_main() {
 
  // initialize NVS flash
 
  ESP_ERROR_CHECK(nvs_flash_init());
 

	
 
  // initialize networking
 
  ESP_ERROR_CHECK(esp_netif_init());
 

	
 
  // create default event loop
 
  ESP_ERROR_CHECK(esp_event_loop_create_default());
 

	
 
  // enable Wi-Fi
 
  esp_netif_create_default_wifi_sta();
 

	
 
  // initialize Wi-Fi
 
  wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
 
  ESP_ERROR_CHECK(esp_wifi_init(&cfg));
 

	
 
  // set Wi-Fi storage to ram
 
  ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
 

	
 
  // set wifi mode
 
  ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
 

	
 
  // register event handlers
 
  ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &handler, NULL, NULL));
 
  ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &handler, NULL, NULL));
 

	
 
  // prepare Wi-Fi config
 
  wifi_config_t wifi_config = {.sta = {.ssid = WIFI_SSID, .password = WIFI_PASS}};
 
  ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
 
  ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
 

	
 
  // start Wi-Fi
 
  ESP_ERROR_CHECK(esp_wifi_start());
 

	
 
  // prepare client
 
  esp_osc_init(&client, 1024, 9000);
 

	
 
  // create tasks
 
  xTaskCreatePinnedToCore(sender, "sender", 4096, NULL, 10, NULL, 1);
 
  xTaskCreatePinnedToCore(receiver, "receiver", 4096, NULL, 10, NULL, 1);
 
  xTaskCreatePinnedToCore(restarter, "restarter", 4096, NULL, 10, NULL, 1);
 
}
components/esp-osc/tinyosc.c
Show inline comments
 
new file 100644
 
/**
 
 * Copyright (c) 2015-2018, Martin Roth (mhroth@gmail.com)
 
 *
 
 * Permission to use, copy, modify, and/or distribute this software for any
 
 * purpose with or without fee is hereby granted, provided that the above
 
 * copyright notice and this permission notice appear in all copies.
 
 *
 
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
 
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 
 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 
 * PERFORMANCE OF THIS SOFTWARE.
 
 */
 

	
 
#include <stddef.h>
 
#include <stdarg.h>
 
#include <string.h>
 
#include <stdio.h>
 
#if _WIN32
 
#include <winsock2.h>
 
#define tosc_strncpy(_dst, _src, _len) strncpy_s(_dst, _len, _src, _TRUNCATE)
 
#else
 
#include <netinet/in.h>
 
#define tosc_strncpy(_dst, _src, _len) strncpy(_dst, _src, _len)
 
#endif
 
#if __unix__ && !__APPLE__
 
#include <endian.h>
 
#define htonll(x) htobe64(x)
 
#define ntohll(x) be64toh(x)
 
#endif
 
#include "tinyosc.h"
 

	
 
#define BUNDLE_ID 0x2362756E646C6500L  // "#bundle"
 

	
 
// http://opensoundcontrol.org/spec-1_0
 
int tosc_parseMessage(tosc_message *o, char *buffer, const int len) {
 
  // NOTE(mhroth): if there's a comma in the address, that's weird
 
  int i = 0;
 
  while (buffer[i] != '\0') ++i;  // find the null-terimated address
 
  while (buffer[i] != ',') ++i;   // find the comma which starts the format string
 
  if (i >= len) return -1;        // error while looking for format string
 
  // format string is null terminated
 
  o->format = buffer + i + 1;  // format starts after comma
 

	
 
  while (i < len && buffer[i] != '\0') ++i;
 
  if (i == len) return -2;  // format string not null terminated
 

	
 
  i = (i + 4) & ~0x3;  // advance to the next multiple of 4 after trailing '\0'
 
  o->marker = buffer + i;
 

	
 
  o->buffer = buffer;
 
  o->len = len;
 

	
 
  return 0;
 
}
 

	
 
// check if first eight bytes are '#bundle '
 
bool tosc_isBundle(const char *buffer) { return ((*(const int64_t *)buffer) == htonll(BUNDLE_ID)); }
 

	
 
void tosc_parseBundle(tosc_bundle *b, char *buffer, const int len) {
 
  b->buffer = (char *)buffer;
 
  b->marker = buffer + 16;  // move past '#bundle ' and timetag fields
 
  b->bufLen = len;
 
  b->bundleLen = len;
 
}
 

	
 
uint64_t tosc_getTimetag(tosc_bundle *b) { return ntohll(*((uint64_t *)(b->buffer + 8))); }
 

	
 
uint32_t tosc_getBundleLength(tosc_bundle *b) { return b->bundleLen; }
 

	
 
bool tosc_getNextMessage(tosc_bundle *b, tosc_message *o) {
 
  if ((b->marker - b->buffer) >= b->bundleLen) return false;
 
  uint32_t len = (uint32_t)ntohl(*((int32_t *)b->marker));
 
  tosc_parseMessage(o, b->marker + 4, len);
 
  b->marker += (4 + len);  // move marker to next bundle element
 
  return true;
 
}
 

	
 
char *tosc_getAddress(tosc_message *o) { return o->buffer; }
 

	
 
char *tosc_getFormat(tosc_message *o) { return o->format; }
 

	
 
uint32_t tosc_getLength(tosc_message *o) { return o->len; }
 

	
 
int32_t tosc_getNextInt32(tosc_message *o) {
 
  // convert from big-endian (network btye order)
 
  const int32_t i = (int32_t)ntohl(*((uint32_t *)o->marker));
 
  o->marker += 4;
 
  return i;
 
}
 

	
 
int64_t tosc_getNextInt64(tosc_message *o) {
 
  const int64_t i = (int64_t)ntohll(*((uint64_t *)o->marker));
 
  o->marker += 8;
 
  return i;
 
}
 

	
 
uint64_t tosc_getNextTimetag(tosc_message *o) { return (uint64_t)tosc_getNextInt64(o); }
 

	
 
float tosc_getNextFloat(tosc_message *o) {
 
  // convert from big-endian (network btye order)
 
  const uint32_t i = ntohl(*((uint32_t *)o->marker));
 
  o->marker += 4;
 
  return *((float *)(&i));
 
}
 

	
 
double tosc_getNextDouble(tosc_message *o) {
 
  const uint64_t i = ntohll(*((uint64_t *)o->marker));
 
  o->marker += 8;
 
  return *((double *)(&i));
 
}
 

	
 
const char *tosc_getNextString(tosc_message *o) {
 
  int i = (int)strlen(o->marker);
 
  if (o->marker + i >= o->buffer + o->len) return NULL;
 
  const char *s = o->marker;
 
  i = (i + 4) & ~0x3;  // advance to next multiple of 4 after trailing '\0'
 
  o->marker += i;
 
  return s;
 
}
 

	
 
void tosc_getNextBlob(tosc_message *o, const char **buffer, int *len) {
 
  int i = (int)ntohl(*((uint32_t *)o->marker));  // get the blob length
 
  if (o->marker + 4 + i <= o->buffer + o->len) {
 
    *len = i;  // length of blob
 
    *buffer = o->marker + 4;
 
    i = (i + 7) & ~0x3;
 
    o->marker += i;
 
  } else {
 
    *len = 0;
 
    *buffer = NULL;
 
  }
 
}
 

	
 
unsigned char *tosc_getNextMidi(tosc_message *o) {
 
  unsigned char *m = (unsigned char *)o->marker;
 
  o->marker += 4;
 
  return m;
 
}
 

	
 
tosc_message *tosc_reset(tosc_message *o) {
 
  int i = 0;
 
  while (o->format[i] != '\0') ++i;
 
  i = (i + 4) & ~0x3;             // advance to the next multiple of 4 after trailing '\0'
 
  o->marker = o->format + i - 1;  // -1 to account for ',' format prefix
 
  return o;
 
}
 

	
 
void tosc_writeBundle(tosc_bundle *b, uint64_t timetag, char *buffer, const int len) {
 
  *((uint64_t *)buffer) = htonll(BUNDLE_ID);
 
  *((uint64_t *)(buffer + 8)) = htonll(timetag);
 

	
 
  b->buffer = buffer;
 
  b->marker = buffer + 16;
 
  b->bufLen = len;
 
  b->bundleLen = 16;
 
}
 

	
 
// always writes a multiple of 4 bytes
 
uint32_t tosc_vwrite(char *buffer, const int len, const char *address, const char *format, va_list ap) {
 
  memset(buffer, 0, len);  // clear the buffer
 
  uint32_t i = (uint32_t)strlen(address);
 
  if (address == NULL || i >= len) return -1;
 
  tosc_strncpy(buffer, address, len);
 
  i = (i + 4) & ~0x3;
 
  buffer[i++] = ',';
 
  int s_len = (int)strlen(format);
 
  if (format == NULL || (i + s_len) >= len) return -2;
 
  tosc_strncpy(buffer + i, format, len - i - s_len);
 
  i = (i + 4 + s_len) & ~0x3;
 

	
 
  for (int j = 0; format[j] != '\0'; ++j) {
 
    switch (format[j]) {
 
      case 'b': {
 
        const uint32_t n = (uint32_t)va_arg(ap, int);  // length of blob
 
        if (i + 4 + n > len) return -3;
 
        char *b = (char *)va_arg(ap, void *);  // pointer to binary data
 
        *((uint32_t *)(buffer + i)) = htonl(n);
 
        i += 4;
 
        memcpy(buffer + i, b, n);
 
        i = (i + 3 + n) & ~0x3;
 
        break;
 
      }
 
      case 'f': {
 
        if (i + 4 > len) return -3;
 
        const float f = (float)va_arg(ap, double);
 
        *((uint32_t *)(buffer + i)) = htonl(*((uint32_t *)&f));
 
        i += 4;
 
        break;
 
      }
 
      case 'd': {
 
        if (i + 8 > len) return -3;
 
        const double f = (double)va_arg(ap, double);
 
        *((uint64_t *)(buffer + i)) = htonll(*((uint64_t *)&f));
 
        i += 8;
 
        break;
 
      }
 
      case 'i': {
 
        if (i + 4 > len) return -3;
 
        const uint32_t k = (uint32_t)va_arg(ap, int);
 
        *((uint32_t *)(buffer + i)) = htonl(k);
 
        i += 4;
 
        break;
 
      }
 
      case 'm': {
 
        if (i + 4 > len) return -3;
 
        const unsigned char *const k = (unsigned char *)va_arg(ap, void *);
 
        memcpy(buffer + i, k, 4);
 
        i += 4;
 
        break;
 
      }
 
      case 't':
 
      case 'h': {
 
        if (i + 8 > len) return -3;
 
        const uint64_t k = (uint64_t)va_arg(ap, long long);
 
        *((uint64_t *)(buffer + i)) = htonll(k);
 
        i += 8;
 
        break;
 
      }
 
      case 's': {
 
        const char *str = (const char *)va_arg(ap, void *);
 
        s_len = (int)strlen(str);
 
        if (i + s_len >= len) return -3;
 
        tosc_strncpy(buffer + i, str, len - i - s_len);
 
        i = (i + 4 + s_len) & ~0x3;
 
        break;
 
      }
 
      case 'T':  // true
 
      case 'F':  // false
 
      case 'N':  // nil
 
      case 'I':  // infinitum
 
        break;
 
      default:
 
        return -4;  // unknown type
 
    }
 
  }
 

	
 
  return i;  // return the total number of bytes written
 
}
 

	
 
uint32_t tosc_writeNextMessage(tosc_bundle *b, const char *address, const char *format, ...) {
 
  va_list ap;
 
  va_start(ap, format);
 
  if (b->bundleLen >= b->bufLen) return 0;
 
  const uint32_t i = tosc_vwrite(b->marker + 4, b->bufLen - b->bundleLen - 4, address, format, ap);
 
  va_end(ap);
 
  *((uint32_t *)b->marker) = htonl(i);  // write the length of the message
 
  b->marker += (4 + i);
 
  b->bundleLen += (4 + i);
 
  return i;
 
}
 

	
 
uint32_t tosc_writeMessage(char *buffer, const int len, const char *address, const char *format, ...) {
 
  va_list ap;
 
  va_start(ap, format);
 
  const uint32_t i = tosc_vwrite(buffer, len, address, format, ap);
 
  va_end(ap);
 
  return i;  // return the total number of bytes written
 
}
 

	
 
void tosc_printOscBuffer(char *buffer, const int len) {
 
  // parse the buffer contents (the raw OSC bytes)
 
  // a return value of 0 indicates no error
 
  tosc_message m;
 
  const int err = tosc_parseMessage(&m, buffer, len);
 
  if (err == 0)
 
    tosc_printMessage(&m);
 
  else
 
    printf("Error while reading OSC buffer: %i\n", err);
 
}
 

	
 
void tosc_printMessage(tosc_message *osc) {
 
  printf("[%lu bytes] %s %s",
 
         osc->len,              // the number of bytes in the OSC message
 
         tosc_getAddress(osc),  // the OSC address string, e.g. "/button1"
 
         tosc_getFormat(osc));  // the OSC format string, e.g. "f"
 

	
 
  for (int i = 0; osc->format[i] != '\0'; i++) {
 
    switch (osc->format[i]) {
 
      case 'b': {
 
        const char *b = NULL;  // will point to binary data
 
        int n = 0;             // takes the length of the blob
 
        tosc_getNextBlob(osc, &b, &n);
 
        printf(" [%i]", n);                                       // print length of blob
 
        for (int j = 0; j < n; ++j) printf("%02X", b[j] & 0xFF);  // print blob bytes
 
        break;
 
      }
 
      case 'm': {
 
        unsigned char *m = tosc_getNextMidi(osc);
 
        printf(" 0x%02X%02X%02X%02X", m[0], m[1], m[2], m[3]);
 
        break;
 
      }
 
      case 'f':
 
        printf(" %g", tosc_getNextFloat(osc));
 
        break;
 
      case 'd':
 
        printf(" %g", tosc_getNextDouble(osc));
 
        break;
 
      case 'i':
 
        printf(" %ld", tosc_getNextInt32(osc));
 
        break;
 
      case 'h':
 
        printf(" %lld", tosc_getNextInt64(osc));
 
        break;
 
      case 't':
 
        printf(" %lld", tosc_getNextTimetag(osc));
 
        break;
 
      case 's':
 
        printf(" %s", tosc_getNextString(osc));
 
        break;
 
      case 'F':
 
        printf(" false");
 
        break;
 
      case 'I':
 
        printf(" inf");
 
        break;
 
      case 'N':
 
        printf(" nil");
 
        break;
 
      case 'T':
 
        printf(" true");
 
        break;
 
      default:
 
        printf(" Unknown format: '%c'", osc->format[i]);
 
        break;
 
    }
 
  }
 
  printf("\n");
 
}
components/esp-osc/tinyosc.h
Show inline comments
 
new file 100644
 
/**
 
 * Copyright (c) 2015-2018, Martin Roth (mhroth@gmail.com)
 
 *
 
 * Permission to use, copy, modify, and/or distribute this software for any
 
 * purpose with or without fee is hereby granted, provided that the above
 
 * copyright notice and this permission notice appear in all copies.
 
 *
 
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
 
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 
 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 
 * PERFORMANCE OF THIS SOFTWARE.
 
 */
 

	
 
#ifndef _TINY_OSC_
 
#define _TINY_OSC_
 

	
 
#include <stdbool.h>
 
#include <stdint.h>
 

	
 
#define TINYOSC_TIMETAG_IMMEDIATELY 1L
 

	
 
#ifdef __cplusplus
 
extern "C" {
 
#endif
 

	
 
#ifndef os_bswap_64
 
#define os_bswap_64(x)                                                                    \
 
  ((uint64_t)((((x)&0xff00000000000000ull) >> 56) | (((x)&0x00ff000000000000ull) >> 40) | \
 
              (((x)&0x0000ff0000000000ull) >> 24) | (((x)&0x000000ff00000000ull) >> 8) |  \
 
              (((x)&0x00000000ff000000ull) << 8) | (((x)&0x0000000000ff0000ull) << 24) |  \
 
              (((x)&0x000000000000ff00ull) << 40) | (((x)&0x00000000000000ffull) << 56)))
 
#endif
 

	
 
#ifndef htonll
 
#define htonll(x) os_bswap_64(x)
 
#endif
 

	
 
#ifndef ntohll
 
#define ntohll htonll
 
#endif
 

	
 
typedef struct tosc_message {
 
  char *format;  // a pointer to the format field
 
  char *marker;  // the current read head
 
  char *buffer;  // the original message data (also points to the address)
 
  uint32_t len;  // length of the buffer data
 
} tosc_message;
 

	
 
typedef struct tosc_bundle {
 
  char *marker;        // the current write head (where the next message will be written)
 
  char *buffer;        // the original buffer
 
  uint32_t bufLen;     // the byte length of the original buffer
 
  uint32_t bundleLen;  // the byte length of the total bundle
 
} tosc_bundle;
 

	
 
/**
 
 * Returns true if the buffer refers to a bundle of OSC messages. False otherwise.
 
 */
 
bool tosc_isBundle(const char *buffer);
 

	
 
/**
 
 * Reads a buffer containing a bundle of OSC messages.
 
 */
 
void tosc_parseBundle(tosc_bundle *b, char *buffer, const int len);
 

	
 
/**
 
 * Returns the timetag of an OSC bundle.
 
 */
 
uint64_t tosc_getTimetag(tosc_bundle *b);
 

	
 
/**
 
 * Parses the next message in a bundle. Returns true if successful.
 
 * False otherwise.
 
 */
 
bool tosc_getNextMessage(tosc_bundle *b, tosc_message *o);
 

	
 
/**
 
 * Returns a point to the address block of the OSC buffer.
 
 * This is also the start of the buffer.
 
 */
 
char *tosc_getAddress(tosc_message *o);
 

	
 
/**
 
 * Returns a pointer to the format block of the OSC buffer.
 
 */
 
char *tosc_getFormat(tosc_message *o);
 

	
 
/**
 
 * Returns the length in bytes of this message.
 
 */
 
uint32_t tosc_getLength(tosc_message *o);
 

	
 
/**
 
 * Returns the next 32-bit int. Does not check buffer bounds.
 
 */
 
int32_t tosc_getNextInt32(tosc_message *o);
 

	
 
/**
 
 * Returns the next 64-bit int. Does not check buffer bounds.
 
 */
 
int64_t tosc_getNextInt64(tosc_message *o);
 

	
 
/**
 
 * Returns the next 64-bit timetag. Does not check buffer bounds.
 
 */
 
uint64_t tosc_getNextTimetag(tosc_message *o);
 

	
 
/**
 
 * Returns the next 32-bit float. Does not check buffer bounds.
 
 */
 
float tosc_getNextFloat(tosc_message *o);
 

	
 
/**
 
 * Returns the next 64-bit float. Does not check buffer bounds.
 
 */
 
double tosc_getNextDouble(tosc_message *o);
 

	
 
/**
 
 * Returns the next string, or NULL if the buffer length is exceeded.
 
 */
 
const char *tosc_getNextString(tosc_message *o);
 

	
 
/**
 
 * Points the given buffer pointer to the next blob.
 
 * The len pointer is set to the length of the blob.
 
 * Returns NULL and 0 if the OSC buffer bounds are exceeded.
 
 */
 
void tosc_getNextBlob(tosc_message *o, const char **buffer, int *len);
 

	
 
/**
 
 * Returns the next set of midi bytes. Does not check bounds.
 
 * Bytes from MSB to LSB are: port id, status byte, data1, data2.
 
 */
 
unsigned char *tosc_getNextMidi(tosc_message *o);
 

	
 
/**
 
 * Resets the read head to the first element.
 
 *
 
 * @return  The same tosc_message pointer.
 
 */
 
tosc_message *tosc_reset(tosc_message *o);
 

	
 
/**
 
 * Parse a buffer containing an OSC message.
 
 * The contents of the buffer are NOT copied.
 
 * The tosc_message struct only points at relevant parts of the original buffer.
 
 * Returns 0 if there is no error. An error code (a negative number) otherwise.
 
 */
 
int tosc_parseMessage(tosc_message *o, char *buffer, const int len);
 

	
 
/**
 
 * Starts writing a bundle to the given buffer with length.
 
 */
 
void tosc_writeBundle(tosc_bundle *b, uint64_t timetag, char *buffer, const int len);
 

	
 
/**
 
 * Write a message to a bundle buffer. Returns the number of bytes written.
 
 */
 
uint32_t tosc_writeNextMessage(tosc_bundle *b, const char *address, const char *format, ...);
 

	
 
/**
 
 * Returns the length in bytes of the bundle.
 
 */
 
uint32_t tosc_getBundleLength(tosc_bundle *b);
 

	
 
/**
 
 * Writes an OSC packet to a buffer. Returns the total number of bytes written.
 
 * The entire buffer is cleared before writing.
 
 */
 
uint32_t tosc_writeMessage(char *buffer, const int len, const char *address, const char *fmt, ...);
 

	
 
/**
 
 * A convenience function to (non-destructively) print a buffer containing
 
 * an OSC message to stdout.
 
 */
 
void tosc_printOscBuffer(char *buffer, const int len);
 

	
 
/**
 
 * A convenience function to (non-destructively) print a pre-parsed OSC message
 
 * to stdout.
 
 */
 
void tosc_printMessage(tosc_message *o);
 

	
 
uint32_t tosc_vwrite(char *buffer, const int len, const char *address, const char *format, va_list ap);
 

	
 
#ifdef __cplusplus
 
}
 
#endif
 

	
 
#endif  // _TINY_OSC_
main/CMakeLists.txt
Show inline comments
 
idf_component_register(SRCS "main.c" "wifi.c" "usb_cdc.c" "can.c" "display.c" "display_gui.c"
 
idf_component_register(SRCS "main.c" "wifi.c" "usb_cdc.c" "can.c" "display.c" "display_gui.c" "osc_control.c"
 
                       INCLUDE_DIRS .)
main/display_gui.c
Show inline comments
 
@@ -41,13 +41,23 @@ void display_gui_homescreen(void)
 
    display_lock(0);
 

	
 
    label = lv_label_create(lv_scr_act());
 
    lv_label_set_text(label, "Yup");
 
    lv_label_set_text(label, "Boot Complete");
 
    // lv_obj_align_to(label, lv_scr_act(), LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
 

	
 
        // 14
 
        // 20
 
        // 24
 
        // 28
 
        // montserrat
 

	
 

	
 
    // lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_FONT_MONTSERRAT_28);
 
    lv_obj_set_style_text_font(label, &lv_font_montserrat_28, LV_PART_MAIN| LV_STATE_DEFAULT);
 

	
 

	
 
    // display_slider();
 
    //Call one at a time to see examples
 
    display_meter();
 
    //display_meter();
 
    // display_image();
 
    // display_window();
 
    //display_dropdown();
 
@@ -56,6 +66,24 @@ void display_gui_homescreen(void)
 
}
 

	
 

	
 
// TODO: make a display task that listens for messages from a queue that change visual elements
 
uint32_t value = 0;
 
void display_gui_process(void)
 
{
 
    
 
    display_lock(0);
 

	
 
    //set_value(meter_indic1, value);
 
    
 
    //value = (value + 1) % 100;
 

	
 
    display_unlock();
 

	
 
}
 

	
 

	
 

	
 

	
 

	
 

	
 

	
 
@@ -236,23 +264,6 @@ void display_update_text(char* str)
 

	
 

	
 

	
 
// make a display task that listens for messages from a queue that change visual elements
 
uint32_t value = 0;
 

	
 
void display_gui_process(void)
 
{
 
    
 
    display_lock(0);
 

	
 
    set_value(meter_indic1, value);
 
    
 
    value = (value + 1) % 100;
 

	
 
    display_unlock();
 

	
 
}
 

	
 

	
 

	
 

	
 

	
main/main.c
Show inline comments
 
@@ -11,7 +11,7 @@
 
#include "esp_log.h"
 
#include "nvs_flash.h"
 
#include "sdkconfig.h"
 

	
 
#include "osc_control.h"
 
#include "wifi.h"
 
// #include "usb_cdc.h"
 
#include "can.h"
 
@@ -47,6 +47,9 @@ void app_main(void)
 
    // Connect to wifi
 
    wifi_init();
 

	
 
    // Initialize OSC
 
    osc_init();
 

	
 
    // Initialize canbus
 
    //can_init();
 

	
main/osc_control.c
Show inline comments
 
new file 100644
 
#include "osc_control.h"
 
#include <stdlib.h>
 
#include <esp_event.h>
 
#include <esp_log.h>
 
#include <esp_wifi.h>
 
#include "display.h"
 
#include <esp_osc.h>
 

	
 
#define WIFI_SSID ""
 
#define WIFI_PASS ""
 

	
 
#define OSC_ADDRESS ""
 
#define OSC_PORT 0
 

	
 
static const char *TAG = "osc_control";
 

	
 
esp_osc_client_t client;
 

	
 
static void sender() {
 
  // select targets
 
  esp_osc_target_t targets[2] = {
 
      esp_osc_target("127.0.0.1", 9000),
 
      esp_osc_target(OSC_ADDRESS, OSC_PORT),
 
  };
 

	
 
  for (;;) {
 
    // delay
 
    vTaskDelay(1000 / portTICK_PERIOD_MS);
 

	
 
    // send messages
 
    for (size_t i = 0; i < 2; i++) {
 
      esp_osc_send(&client, &targets[i], "test", "ihfdsb", 42, (int64_t)84, 3.14f, 6.28, "foo", 3, "bar");
 
    }
 
  }
 
}
 

	
 
static bool callback(const char *topic, const char *format, esp_osc_value_t *values) {
 
  // log message
 
  ESP_LOGI(TAG, "got message: %s (%s)", topic, format);
 

	
 

	
 
  char out[512] = {0};
 
  snprintf(out, 128, "topic: %s\nformat: %s\n", topic, format);
 

	
 
  for (size_t i = 0; i < strlen(format); i++) {
 
    switch (format[i]) {
 
      case 'i':
 
        ESP_LOGI(TAG, "==> i: %ld", values[i].i);
 
        break;
 
      case 'h':
 
        ESP_LOGI(TAG, "==> h: %lld", values[i].h);
 
        break;
 
      case 'f':
 
        snprintf(out+strlen(out), 128-strlen(out), "Value: %f", values[i].f);
 
        ESP_LOGI(TAG, "==> f: %f", values[i].f);
 
        break;
 
      case 'd':
 
        ESP_LOGI(TAG, "==> d: %f", values[i].d);
 
        break;
 
      case 's':
 
        ESP_LOGI(TAG, "==> s: %s", values[i].s);
 
        break;
 
      case 'b':
 
        ESP_LOGI(TAG, "==> b: %.*s (%d)", values[i].bl, values[i].b, values[i].bl);
 
        break;
 
    }
 
  }
 

	
 
  display_update_text(out);
 

	
 
  return true;
 
}
 

	
 
static void receiver() {
 
  for (;;) {
 
    // receive messages
 
    esp_osc_receive(&client, callback);
 
  }
 
}
 

	
 
static void restarter() {
 
  for (;;) {
 
    // delay
 
    vTaskDelay(5000 / portTICK_PERIOD_MS);
 

	
 
    // restart client
 
    esp_osc_init(&client, 1024, 9000);
 
  }
 
}
 

	
 

	
 
void osc_init() {
 
  // prepare client
 
  esp_osc_init(&client, 1024, 5005);
 

	
 
  // create tasks
 
  //xTaskCreatePinnedToCore(sender, "sender", 4096, NULL, 10, NULL, 1);
 
  xTaskCreatePinnedToCore(receiver, "receiver", 4096, NULL, 10, NULL, 1);
 
  //xTaskCreatePinnedToCore(restarter, "restarter", 4096, NULL, 10, NULL, 1);
 
}
 
\ No newline at end of file
main/osc_control.h
Show inline comments
 
new file 100644
 

	
 
#ifndef _OSC_CONTROL_H_
 
#define _OSC_CONTROL_H_
 

	
 

	
 
void osc_init();
 

	
 

	
 
#endif // _OSC_CONTROL_H_
main/wifi.c
Show inline comments
 
@@ -33,8 +33,8 @@ static void __event_handler(void* arg, e
 
        {
 
            esp_wifi_connect();
 
            s_retry_num++;
 
            ESP_LOGI(TAG, "retry to connect to the AP");
 
            //display_update_text("AP Connect Retry");
 
                ESP_LOGI(TAG, "retry to connect to the AP");
 
            display_update_text("AP Connect Retry");
 

	
 
        } 
 
        else 
 
@@ -42,7 +42,7 @@ static void __event_handler(void* arg, e
 
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
 
        }
 
        ESP_LOGI(TAG,"connect to the AP fail");
 
        //display_update_text("AP Connect Fail");
 
        display_update_text("AP Connect Fail");
 
        
 
    } 
 
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) 
 
@@ -50,7 +50,7 @@ static void __event_handler(void* arg, e
 
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
 
        ESP_LOGI(TAG, "WIFI OK, IP: " IPSTR, IP2STR(&event->ip_info.ip));
 
        char out[128] = {0};
 
        snprintf(out, 128, "WIFI OK, IP: " IPSTR, IP2STR(&event->ip_info.ip));
 
        snprintf(out, 128, "WIFI OK\nIP: " IPSTR, IP2STR(&event->ip_info.ip));
 
        display_update_text(out);
 

	
 

	
0 comments (0 inline, 0 general)