--- /dev/null
+Assignment of peripherals and pins
+==================================
+
+I2C1 BMP085
+I2C2 display
+USART1 debugging
+
+
+ Blue Pill pinout
+ +--------------------+
+ | VBATT 3.3V |
+BluePill LED | PC13 GND |
+ | PC14 5V |
+ | PC15 PB9 |
+ | PA0 PB8 |
+ | PA1 PB7 | BMP085 SDA
+ | PA2 PB6 | BMP085 SCL
+ | PA3 PB5 | BMP085 conversion done
+ | PA4 PB4 |
+ | PA5 PB3 |
+ | PA6 PA15 |
+ | PA7 PA12 |
+ | PB0 PA11 |
+ | PB1 PA10 | RXD1 - debugging console
+display SCL | PB10 PA9 | TXD1 - debugging console
+display SDA | PB11 PA8 |
+ | RESET PB15 |
+ | 3.3 V PB14 |
+ | GND PB13 |
+ | GND PB12 |
+ +--------------------+
--- /dev/null
+ROOT=../..
+BINARY=clock
+OBJS=main.o
+LIB_OBJS=util-debug.o ds18b20.o
+
+include $(ROOT)/mk/bluepill.mk
--- /dev/null
+/*
+ * Burrow Clock -- Configuration
+ *
+ * (c) 2019 Martin Mareš <mj@ucw.cz>
+ */
+
+// Processor clock
+
+#define CPU_CLOCK_MHZ 72
+
+// Debugging port
+
+#define DEBUG_USART USART1
+#define DEBUG_LED_BLUEPILL
--- /dev/null
+set port /dev/ttyUSB0
+set speed 115200
+set flow-control none
+set carrier-watch off
+connect
--- /dev/null
+/*
+ * Clock with Barometer
+ *
+ * (c) 2018--2019 Martin Mareš <mj@ucw.cz>
+ */
+
+FIXME: Partial code...
+
+#include "util.h"
+
+#include <libopencm3/cm3/nvic.h>
+#include <libopencm3/cm3/systick.h>
+#include <libopencm3/stm32/rcc.h>
+#include <libopencm3/stm32/gpio.h>
+#include <libopencm3/stm32/timer.h>
+#include <libopencm3/stm32/usart.h>
+
+#include <string.h>
+
+static void clock_init(void)
+{
+ rcc_clock_setup_in_hse_8mhz_out_72mhz();
+
+ rcc_periph_clock_enable(RCC_GPIOB);
+ rcc_periph_clock_enable(RCC_GPIOC);
+ rcc_periph_clock_enable(RCC_USART1);
+ rcc_periph_clock_enable(RCC_USB);
+
+ rcc_periph_reset_pulse(RST_GPIOB);
+ rcc_periph_reset_pulse(RST_GPIOC);
+ rcc_periph_reset_pulse(RST_USART1);
+ rcc_periph_reset_pulse(RST_USB);
+}
+
+static void gpio_init(void)
+{
+ // Switch JTAG off to free up pins
+ gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, 0);
+
+ // PA9 = TXD1 for debugging console
+ gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9);
+
+ // PA10 = RXD1 for debugging console
+ gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO10);
+
+ // PC13 = BluePill LED
+ gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
+ gpio_clear(GPIOC, GPIO13);
+}
+
+static volatile u32 ms_ticks;
+
+void sys_tick_handler(void)
+{
+ ms_ticks++;
+}
+
+static void tick_init(void)
+{
+ systick_set_frequency(1000, 72000000);
+ systick_counter_enable();
+ systick_interrupt_enable();
+}
+
+#if 0
+static void delay_ms(uint ms)
+{
+ u32 start_ticks = ms_ticks;
+ while (ms_ticks - start_ticks < ms)
+ ;
+}
+#endif
+
+static void usart_init(void)
+{
+ usart_set_baudrate(USART1, 115200);
+ usart_set_databits(USART1, 8);
+ usart_set_stopbits(USART1, USART_STOPBITS_1);
+ usart_set_mode(USART1, USART_MODE_TX_RX);
+ usart_set_parity(USART1, USART_PARITY_NONE);
+ usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
+
+ usart_enable(USART1);
+}
+
+static const struct usb_device_descriptor dev = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = 0x0200,
+ .bDeviceClass = 0xFF,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .bMaxPacketSize0 = 64,
+ .idVendor = 0xCAFE,
+ .idProduct = 0xCAFE,
+ .bcdDevice = 0x0200,
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .iSerialNumber = 3,
+ .bNumConfigurations = 1,
+};
+
+static const struct usb_endpoint_descriptor endpoints[] = {{
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0x81,
+ .bmAttributes = USB_ENDPOINT_ATTR_BULK,
+ .wMaxPacketSize = 64,
+ .bInterval = 1,
+}};
+
+static const struct usb_interface_descriptor iface = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = 0xFF,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .iInterface = 0,
+ .endpoint = endpoints,
+};
+
+static const struct usb_interface ifaces[] = {{
+ .num_altsetting = 1,
+ .altsetting = &iface,
+}};
+
+static const struct usb_config_descriptor config = {
+ .bLength = USB_DT_CONFIGURATION_SIZE,
+ .bDescriptorType = USB_DT_CONFIGURATION,
+ .wTotalLength = 0,
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = 0x80,
+ .bMaxPower = 50, // multiplied by 2 mA
+ .interface = ifaces,
+};
+
+static const char *usb_strings[] = {
+ "Hippo Computing Inc.",
+ "Space Alert Thermometer",
+ "42",
+};
+
+uint8_t usbd_control_buffer[64];
+
+static void ep81_cb(usbd_device *usbd_dev, uint8_t ep UNUSED)
+{
+ byte buf[4];
+ if (ds_sensors[0].address[0] && ds_sensors[0].current_temp != DS_TEMP_UNKNOWN) {
+ put_u32_be(buf, (u32) ds_sensors[0].current_temp);
+ } else {
+ put_u32_be(buf, 0x80000000);
+ }
+ usbd_ep_write_packet(usbd_dev, 0x81, buf, sizeof(buf));
+}
+
+static void set_config_cb(usbd_device *usbd_dev, uint16_t wValue UNUSED)
+{
+ usbd_ep_setup(usbd_dev, 0x81, USB_ENDPOINT_ATTR_BULK, 64, ep81_cb);
+ ep81_cb(usbd_dev, 0);
+}
+
+int main(void)
+{
+ clock_init();
+ gpio_init();
+ tick_init();
+ usart_init();
+
+ // Simulate USB disconnect
+ gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO11 | GPIO12);
+ gpio_clear(GPIOA, GPIO11 | GPIO12);
+ delay_ms(1000);
+
+ usbd_device *usbd_dev = usbd_init(&st_usbfs_v1_usb_driver, &dev, &config, usb_strings, 4, usbd_control_buffer, sizeof(usbd_control_buffer));
+ usbd_register_set_config_callback(usbd_dev, set_config_cb);
+ u32 last_ds_step = 0;
+
+ for (;;) {
+ if (ms_ticks - last_ds_step >= 100) {
+ debug_led_toggle();
+ ds_step();
+ last_ds_step = ms_ticks;
+ }
+
+ // XXX: libopencm3 usbd does not use interrupts at the moment, need to poll...
+ // wait_for_interrupt();
+ usbd_poll(usbd_dev);
+ }
+
+ return 0;
+}
--- /dev/null
+UCWCF:=$(shell PKG_CONFIG_PATH=$(LIBUCW)/lib/pkgconfig pkg-config --cflags libucw)
+UCWLF:=$(shell PKG_CONFIG_PATH=$(LIBUCW)/lib/pkgconfig pkg-config --libs libucw)
+
+CFLAGS=-std=gnu99 -O2 -Wall -Wextra -Wno-parentheses $(UCWCF)
+LDLIBS=-lusb-1.0 $(UCWLF)
+
+all: test burrow-clock
+
+test: test.c
+
+burrow-clock: burrow-clock.c
+burrow-clock: LDLIBS += -lmosquitto
+
+install: all
+ install burrow-clock /usr/local/sbin/
+
+clean:
+ rm -f burrow-clock test
--- /dev/null
+/*
+ * MJ's desktop clock daemon
+ *
+ * (c) 2018 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/log.h>
+#include <ucw/opt.h>
+#include <ucw/unaligned.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <libusb-1.0/libusb.h>
+#include <mosquitto.h>
+
+static struct mosquitto *mosq;
+
+struct libusb_context *usb_ctxt;
+struct libusb_device_handle *devh;
+
+static libusb_device *find_device(void)
+{
+ libusb_device **devlist;
+ ssize_t devn = libusb_get_device_list(usb_ctxt, &devlist);
+ if (devn < 0) {
+ fprintf(stderr, "Cannot enumerate USB devices: error %d\n", (int) devn);
+ exit(1);
+ }
+
+ for (ssize_t i=0; i<devn; i++) {
+ struct libusb_device_descriptor desc;
+ libusb_device *dev = devlist[i];
+ if (!libusb_get_device_descriptor(dev, &desc)) {
+ if (desc.idVendor == 0x4242 && desc.idProduct == 0x0001) {
+ msg(L_INFO, "Found device at usb%d.%d", libusb_get_bus_number(dev), libusb_get_device_address(dev));
+ // FIXME: Free device list
+ return dev;
+ }
+ }
+ }
+
+ libusb_free_device_list(devlist, 1);
+ fprintf(stderr, "Device not found\n");
+ exit(1);
+}
+
+static void mqtt_publish(const char *topic, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ char m[256];
+ int l = vsnprintf(m, sizeof(m), fmt, args);
+ if (mosquitto_publish(mosq, NULL, topic, l, m, 0, true) != MOSQ_ERR_SUCCESS)
+ msg(L_ERROR, "Mosquitto: publish failed");
+ va_end(args);
+}
+
+static void mqtt_conn_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, int status)
+{
+ if (!status) {
+ msg(L_DEBUG, "MQTT: Connection established");
+ mqtt_publish("status/clock", "ok");
+ }
+}
+
+static int use_daemon;
+static int use_debug;
+
+static struct opt_section options = {
+ OPT_ITEMS {
+ OPT_HELP("A daemon for controlling the solid state relay module via MQTT"),
+ OPT_HELP(""),
+ OPT_HELP("Options:"),
+ OPT_BOOL('d', "debug", use_debug, 0, "\tLog debugging messages"),
+ OPT_BOOL(0, "daemon", use_daemon, 0, "\tDaemonize"),
+ OPT_HELP_OPTION,
+ OPT_CONF_OPTIONS,
+ OPT_END
+ }
+};
+
+int main(int argc UNUSED, char **argv)
+{
+ log_init(argv[0]);
+ opt_parse(&options, argv+1);
+
+ if (use_daemon) {
+ struct log_stream *ls = log_new_syslog("daemon", LOG_PID);
+ log_set_default_stream(ls);
+ }
+ if (!use_debug)
+ log_default_stream()->levels &= ~(1U << L_DEBUG);
+
+ mosquitto_lib_init();
+ mosq = mosquitto_new("clock", 1, NULL);
+ if (!mosq)
+ die("Mosquitto: initialization failed");
+
+ mosquitto_connect_callback_set(mosq, mqtt_conn_callback);
+
+ if (mosquitto_will_set(mosq, "status/clock", 4, "dead", 0, true) != MOSQ_ERR_SUCCESS)
+ die("Mosquitto: unable to set will");
+
+ if (mosquitto_connect_async(mosq, "10.32.184.5", 1883, 60) != MOSQ_ERR_SUCCESS)
+ die("Mosquitto: connect failed");
+
+ if (mosquitto_loop_start(mosq))
+ die("Mosquitto: cannot start service thread");
+
+ int err;
+ if (err = libusb_init(&usb_ctxt))
+ die("Cannot initialize libusb: error %d", err);
+
+ libusb_device *dev = find_device();
+
+ if (err = libusb_open(dev, &devh))
+ die("Cannot open device: error %d", err);
+ libusb_reset_device(devh);
+ if (err = libusb_claim_interface(devh, 0))
+ die("Cannot claim interface: error %d", err);
+
+ uint cnt = 0;
+ for (;;) {
+ time_t t = time(NULL);
+ struct tm *tm = localtime(&t);
+
+ unsigned char req[8] = {
+ tm->tm_hour / 10,
+ tm->tm_hour % 10,
+ (tm->tm_sec % 2 ? 10 : 0xff),
+ tm->tm_min / 10,
+ tm->tm_min % 10,
+ };
+ int transferred;
+ if (err = libusb_bulk_transfer(devh, 0x01, req, 8, &transferred, 2000))
+ die("Transfer failed: error %d", err);
+
+ unsigned char resp[64];
+ int received;
+ if (err = libusb_bulk_transfer(devh, 0x82, resp, 64, &received, 2000))
+ die("Receive failed: error %d", err);
+ // printf("Received %d bytes\n", received);
+ if (received >= 12) {
+ int temp = get_u32_be(resp);
+ int press = get_u32_be(resp + 4);
+ uint cycle = get_u32_be(resp + 8);
+ msg(L_DEBUG, "Temperature %d ddegC, pressure %d Pa, cycle %u", temp, press, cycle);
+ if (!(cnt % 10) && press) {
+ mqtt_publish("burrow/temp/clock", "%.1f %llu", temp / 10., (long long) t);
+ mqtt_publish("burrow/pressure/clock", "%d %llu", press, (long long) t);
+ }
+ }
+
+ sleep(1);
+ cnt++;
+ }
+
+ return 0;
+}
--- /dev/null
+#include <ucw/lib.h>
+#include <ucw/unaligned.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <time.h>
+#include <libusb-1.0/libusb.h>
+
+struct libusb_context *usb_ctxt;
+struct libusb_device_handle *devh;
+
+static libusb_device *find_device(void)
+{
+ libusb_device **devlist;
+ ssize_t devn = libusb_get_device_list(usb_ctxt, &devlist);
+ if (devn < 0)
+ {
+ fprintf(stderr, "Cannot enumerate USB devices: error %d\n", (int) devn);
+ exit(1);
+ }
+
+ for (ssize_t i=0; i<devn; i++)
+ {
+ struct libusb_device_descriptor desc;
+ libusb_device *dev = devlist[i];
+ if (!libusb_get_device_descriptor(dev, &desc))
+ {
+ if (desc.idVendor == 0x4242 && desc.idProduct == 0x0001)
+ {
+ printf("Found device at usb%d.%d\n", libusb_get_bus_number(dev), libusb_get_device_address(dev));
+ // FIXME: Free device list
+ return dev;
+ }
+ }
+ }
+
+ libusb_free_device_list(devlist, 1);
+ fprintf(stderr, "Device not found\n");
+ exit(1);
+}
+
+int main(void)
+{
+ int err;
+ if (err = libusb_init(&usb_ctxt))
+ {
+ fprintf(stderr, "Cannot initialize libusb: error %d\n", err);
+ exit(1);
+ }
+ // libusb_set_debug(usb_ctxt, 3);
+
+ libusb_device *dev = find_device();
+
+ if (err = libusb_open(dev, &devh))
+ {
+ fprintf(stderr, "Cannot open device: error %d\n", err);
+ exit(1);
+ }
+ libusb_reset_device(devh);
+ if (err = libusb_claim_interface(devh, 0))
+ {
+ fprintf(stderr, "Cannot claim interface: error %d\n", err);
+ exit(1);
+ }
+
+ for (;;)
+ {
+ time_t t = time(NULL);
+ struct tm *tm = localtime(&t);
+
+ unsigned char req[8] = {
+ tm->tm_hour / 10,
+ tm->tm_hour % 10,
+ (tm->tm_sec % 2 ? 10 : 0xff),
+ tm->tm_min / 10,
+ tm->tm_min % 10,
+ };
+ int transferred;
+ if (err = libusb_bulk_transfer(devh, 0x01, req, 8, &transferred, 2000))
+ die("Transfer failed: error %d", err);
+ // printf("Transferred %d bytes\n", transferred);
+
+ unsigned char resp[64];
+ int received;
+ if (err = libusb_bulk_transfer(devh, 0x82, resp, 64, &received, 2000))
+ die("Receive failed: error %d", err);
+ // printf("Received %d bytes\n", received);
+ if (received >= 12)
+ {
+ int t = get_u32_be(resp);
+ int p = get_u32_be(resp + 4);
+ uint cnt = get_u32_be(resp + 8);
+ msg(L_INFO, "Temperature %d ddegC, pressure %d Pa, cnt %u", t, p, cnt);
+ }
+
+ sleep(1);
+ }
+
+ return 0;
+}