From bb58f859e53b362655b82fd20616e1bed6d3f758 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Sun, 23 Feb 2020 12:11:15 +0100 Subject: [PATCH] Clock firmware and host daemon --- clock/README | 31 ++++++ clock/firmware/Makefile | 6 ++ clock/firmware/config.h | 14 +++ clock/firmware/kerm | 5 + clock/firmware/main.c | 196 ++++++++++++++++++++++++++++++++++++++ clock/host/Makefile | 18 ++++ clock/host/burrow-clock.c | 165 ++++++++++++++++++++++++++++++++ clock/host/test.c | 102 ++++++++++++++++++++ 8 files changed, 537 insertions(+) create mode 100644 clock/README create mode 100644 clock/firmware/Makefile create mode 100644 clock/firmware/config.h create mode 100644 clock/firmware/kerm create mode 100644 clock/firmware/main.c create mode 100644 clock/host/Makefile create mode 100644 clock/host/burrow-clock.c create mode 100644 clock/host/test.c diff --git a/clock/README b/clock/README new file mode 100644 index 0000000..8252003 --- /dev/null +++ b/clock/README @@ -0,0 +1,31 @@ +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 | + +--------------------+ diff --git a/clock/firmware/Makefile b/clock/firmware/Makefile new file mode 100644 index 0000000..c54fa11 --- /dev/null +++ b/clock/firmware/Makefile @@ -0,0 +1,6 @@ +ROOT=../.. +BINARY=clock +OBJS=main.o +LIB_OBJS=util-debug.o ds18b20.o + +include $(ROOT)/mk/bluepill.mk diff --git a/clock/firmware/config.h b/clock/firmware/config.h new file mode 100644 index 0000000..24b1b42 --- /dev/null +++ b/clock/firmware/config.h @@ -0,0 +1,14 @@ +/* + * Burrow Clock -- Configuration + * + * (c) 2019 Martin Mareš + */ + +// Processor clock + +#define CPU_CLOCK_MHZ 72 + +// Debugging port + +#define DEBUG_USART USART1 +#define DEBUG_LED_BLUEPILL diff --git a/clock/firmware/kerm b/clock/firmware/kerm new file mode 100644 index 0000000..988f567 --- /dev/null +++ b/clock/firmware/kerm @@ -0,0 +1,5 @@ +set port /dev/ttyUSB0 +set speed 115200 +set flow-control none +set carrier-watch off +connect diff --git a/clock/firmware/main.c b/clock/firmware/main.c new file mode 100644 index 0000000..b2b7cc3 --- /dev/null +++ b/clock/firmware/main.c @@ -0,0 +1,196 @@ +/* + * Clock with Barometer + * + * (c) 2018--2019 Martin Mareš + */ + +FIXME: Partial code... + +#include "util.h" + +#include +#include +#include +#include +#include +#include + +#include + +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; +} diff --git a/clock/host/Makefile b/clock/host/Makefile new file mode 100644 index 0000000..d99cfd4 --- /dev/null +++ b/clock/host/Makefile @@ -0,0 +1,18 @@ +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 diff --git a/clock/host/burrow-clock.c b/clock/host/burrow-clock.c new file mode 100644 index 0000000..4055743 --- /dev/null +++ b/clock/host/burrow-clock.c @@ -0,0 +1,165 @@ +/* + * MJ's desktop clock daemon + * + * (c) 2018 Martin Mares + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +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; ilevels &= ~(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; +} diff --git a/clock/host/test.c b/clock/host/test.c new file mode 100644 index 0000000..30426fb --- /dev/null +++ b/clock/host/test.c @@ -0,0 +1,102 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +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; itm_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; +} -- 2.39.2