Compare commits
2 commits
d54c904c1b
...
3126e777bf
Author | SHA1 | Date | |
---|---|---|---|
3126e777bf | |||
90b0c24d36 |
8 changed files with 227 additions and 1 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -3,3 +3,5 @@ secrets.yaml
|
|||
.esphome/
|
||||
dist/
|
||||
__pycache__/
|
||||
build/
|
||||
tmp.*
|
||||
|
|
|
@ -39,7 +39,7 @@ update:
|
|||
- platform: http_request
|
||||
id: auto_update
|
||||
source: "https://juju.nz/michaelh/assets/esphome/${project_name}.json"
|
||||
update_interval: 2min
|
||||
update_interval: 4hours
|
||||
|
||||
interval:
|
||||
- interval: 50s
|
||||
|
|
6
interceptor/CMakeLists.txt
Normal file
6
interceptor/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.13.1)
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
|
||||
project(app LANGUAGES CXX)
|
||||
|
||||
target_sources(app PRIVATE src/main.cc)
|
8
interceptor/README.md
Normal file
8
interceptor/README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Interceptor
|
||||
|
||||
A passive digital signal sampler for the CH32V003. Tracks the levels of PC1 and PC2 and sends any
|
||||
changes over serial to the host. These can be converted to a VCD format file and analyzed in
|
||||
sigrock.
|
||||
|
||||
Designed for sniffing I2C traffic. PC1 and PC2 are 5 V tollerant and have pullups enabled. Tested at
|
||||
up to 200 kHz.
|
48
interceptor/boards/wch_ch32v003evt.overlay
Normal file
48
interceptor/boards/wch_ch32v003evt.overlay
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include <zephyr/dt-bindings/pinctrl/ch32v003-pinctrl.h>
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
zephyr,console = &usart1;
|
||||
zephyr,shell-uart = &usart1;
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
|
||||
red_led: led0 {
|
||||
gpios = <&gpioa 1 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
||||
scl_pin: scl_pin {
|
||||
gpios = <&gpioc 2 GPIO_PULL_UP>;
|
||||
};
|
||||
|
||||
sda_pin: sda_pin {
|
||||
gpios = <&gpioc 1 GPIO_PULL_UP>;
|
||||
};
|
||||
};
|
||||
|
||||
aliases {
|
||||
led0 = &red_led;
|
||||
scl = &scl_pin;
|
||||
sda = &sda_pin;
|
||||
};
|
||||
};
|
||||
|
||||
&pinctrl {
|
||||
};
|
||||
|
||||
&gpioa {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&gpioc {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&usart1 {
|
||||
status = "okay";
|
||||
current-speed = <460800>;
|
||||
pinctrl-0 = <&usart1_default>;
|
||||
pinctrl-names = "default";
|
||||
};
|
25
interceptor/prj.conf
Normal file
25
interceptor/prj.conf
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Trim back the memory usage.
|
||||
CONFIG_THREAD_STACK_INFO=n
|
||||
CONFIG_ERRNO=n
|
||||
CONFIG_CURRENT_THREAD_USE_TLS=n
|
||||
CONFIG_TIMESLICING=n
|
||||
CONFIG_KERNEL_MEM_POOL=n
|
||||
CONFIG_COMMON_LIBC_MALLOC=n
|
||||
CONFIG_MINIMAL_LIBC=y
|
||||
CONFIG_CBPRINTF_NANO=y
|
||||
CONFIG_CBPRINTF_REDUCED_INTEGRAL=y
|
||||
CONFIG_CBPRINTF_N_SPECIFIER=n
|
||||
|
||||
CONFIG_WCH_CH32V00X_HSI=y
|
||||
CONFIG_WCH_CH32V00X_HSE=n
|
||||
CONFIG_WCH_CH32V00X_HSI_AS_PLLSRC=y
|
||||
|
||||
CONFIG_GPIO=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_CONSOLE=y
|
||||
CONFIG_UART_CONSOLE=y
|
||||
|
||||
CONFIG_CPP=y
|
||||
CONFIG_STD_CPP2B=y
|
||||
|
||||
CONFIG_MAIN_STACK_SIZE=1024
|
88
interceptor/src/main.cc
Normal file
88
interceptor/src/main.cc
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Google LLC.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include <ch32_gpio.h>
|
||||
#include <ch32_uart.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
constexpr struct gpio_dt_spec kLed = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
|
||||
constexpr struct gpio_dt_spec kScl = GPIO_DT_SPEC_GET(DT_ALIAS(scl), gpios);
|
||||
constexpr struct gpio_dt_spec kSda = GPIO_DT_SPEC_GET(DT_ALIAS(sda), gpios);
|
||||
|
||||
void sniff()
|
||||
{
|
||||
GPIO_TypeDef *port = GPIOC;
|
||||
USART_TypeDef *uart = USART1;
|
||||
uint8_t fifo[256];
|
||||
int head = 0;
|
||||
int tail = 0;
|
||||
|
||||
// Note that using `int` reduces the code size by removing unneeded truncation.
|
||||
int samples = 0;
|
||||
int sample_count = 0;
|
||||
int last_sample = 0;
|
||||
int unchanged_for = 0;
|
||||
|
||||
csr_clear(mstatus, MSTATUS_IEN);
|
||||
|
||||
for (;;) {
|
||||
static_assert(kScl.port == kSda.port);
|
||||
static_assert(kScl.pin == kSda.pin + 1);
|
||||
int sample = (port->INDR >> kSda.pin) & 3;
|
||||
|
||||
// Record when the value changes, or the value has been unchanged for some time.
|
||||
if (sample != last_sample || ++unchanged_for >= 1024) {
|
||||
samples <<= 2;
|
||||
samples |= sample;
|
||||
last_sample = sample;
|
||||
unchanged_for = 0;
|
||||
|
||||
if (++sample_count >= 3) {
|
||||
// Encode the three samples into valid ASCII.
|
||||
fifo[head++] = samples + ' ';
|
||||
head %= sizeof(fifo);
|
||||
samples = 0;
|
||||
sample_count = 0;
|
||||
}
|
||||
}
|
||||
if (tail != head && (uart->STATR & USART_STATR_TXE) != 0) {
|
||||
uart->DATAR = fifo[tail++];
|
||||
tail %= sizeof(fifo);
|
||||
|
||||
if (tail == 0 && (uart->STATR & USART_STATR_RXNE) != 0 &&
|
||||
uart->DATAR == '~') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
csr_set(mstatus, MSTATUS_IEN);
|
||||
}
|
||||
|
||||
void interceptor()
|
||||
{
|
||||
gpio_pin_configure_dt(&kLed, GPIO_OUTPUT_ACTIVE);
|
||||
gpio_pin_configure_dt(&kScl, GPIO_INPUT);
|
||||
gpio_pin_configure_dt(&kSda, GPIO_INPUT);
|
||||
|
||||
for (;;) {
|
||||
printk("sniff\n");
|
||||
sniff();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(void)
|
||||
{
|
||||
interceptor();
|
||||
return 0;
|
||||
}
|
49
interceptor/tovcd.py
Normal file
49
interceptor/tovcd.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
"""Converts the samples from the board to a VCD file for sigrok.
|
||||
|
||||
Usage:
|
||||
python3 tovcd.py < /dev/ttyUSB0 | tee log.vcd
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import sys
|
||||
|
||||
_HEADER = """
|
||||
$date {date} $end
|
||||
$version juju.nz tovcd.py $end
|
||||
$timescale 1 us $end
|
||||
$scope module libsigrok $end
|
||||
"""
|
||||
|
||||
_MID = """
|
||||
$upscope $end
|
||||
$enddefinitions $end
|
||||
"""
|
||||
|
||||
_IDS = "!#$%&'()"
|
||||
|
||||
|
||||
def main():
|
||||
print(_HEADER.strip().format(date=datetime.datetime.now()))
|
||||
|
||||
for wire in range(2):
|
||||
print(f"$var wire 1 {_IDS[wire]} D{wire} $end")
|
||||
|
||||
print(_MID.strip())
|
||||
|
||||
now = 0
|
||||
last_d = None
|
||||
|
||||
for ch in sys.stdin.read():
|
||||
ch = ord(ch) - 32
|
||||
for i in range(3):
|
||||
now += 1
|
||||
d = (ch >> 5) & 1, (ch >> 4) & 1
|
||||
ch <<= 2
|
||||
if d != last_d:
|
||||
parts = (f"{x}{_IDS[i]}" for i, x in enumerate(d))
|
||||
print(f"#{now}", " ".join(parts))
|
||||
last_d = d
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in a new issue