-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
robert
committed
Aug 4, 2023
1 parent
ee80822
commit 28cdd9d
Showing
16 changed files
with
1,273 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
CFLAGS = -W -Wall -Wextra -Wundef -Wshadow -Wdouble-promotion | ||
CFLAGS += -Wformat-truncation -fno-common -Wconversion | ||
CFLAGS += -g -O2 -ffunction-sections -fdata-sections | ||
CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include | ||
CFLAGS += -D__SAME54P20A__ -Icmsis_sam/include #-Icmsis_sam/xc32/include | ||
#CFLAGS += -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 | ||
CFLAGS += -mcpu=cortex-m4 -mthumb -mfloat-abi=softfp -mfpu=fpv4-sp-d16 | ||
LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map | ||
|
||
SOURCES = main.c syscalls.c startup.c | ||
#SOURCES += cmsis_sam/xc32/ATSAME54P20A/startup_atsame54p20a.c | ||
|
||
SOURCES += mongoose.c net.c packed_fs.c | ||
CFLAGS += -DMG_ENABLE_TCPIP=1 -DMG_ARCH=MG_ARCH_NEWLIB -DMG_ENABLE_CUSTOM_MILLIS=1 -DMG_ENABLE_LINES=1 | ||
CFLAGS += -DMG_ENABLE_DRIVER_SAME54=1 -DMG_ENABLE_CUSTOM_RANDOM=1 -DMG_ENABLE_PACKED_FS=1 $(CFLAGS_EXTRA) | ||
|
||
VCON_API_KEY=IBrJL5K4arSGMiAXbUKWdG6I2gM | ||
|
||
ifeq ($(OS),Windows_NT) | ||
RM = cmd /C del /Q /F | ||
else | ||
RM = rm -rf | ||
endif | ||
|
||
build: firmware.bin | ||
|
||
firmware.elf: cmsis_core cmsis_sam hal.h link.ld Makefile $(SOURCES) | ||
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@ | ||
|
||
firmware.bin: firmware.elf | ||
arm-none-eabi-objcopy -O binary $< $@ | ||
|
||
flash: firmware.bin | ||
bossac -p /dev/cu.usb* -w -v -b $< | ||
|
||
cmsis_core: | ||
git clone --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@ | ||
|
||
cmsis_sam: | ||
curl -sL https://packs.download.microchip.com/Microchip.SAME54_DFP.3.8.234.pack -o $@.zip | ||
mkdir $@ && cd $@ && unzip ../$@.zip | ||
# git clone --depth 1 -b master https://github.com/modm-io/cmsis-header-sam $@ | ||
|
||
clean: | ||
$(RM) firmware.* cmsis_* *.zip | ||
|
||
# Automated test via https://vcon.io/automated-firmware-tests/. Set VCON_API_KEY and update DEVICE_URL | ||
DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/9 | ||
fota: CFLAGS += -DUART_DEBUG=USART1 | ||
fota: firmware.bin | ||
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$< | ||
test: fota | ||
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt | ||
egrep '^tick:.*CPU 180 MHz' /tmp/output.txt | ||
watch: fota | ||
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=999 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
// Copyright (c) 2022 Cesanta Software Limited | ||
// SPDX-License-Identifier: MIT | ||
// | ||
// https://ww1.microchip.com/downloads/aemDocuments/documents/MCU32/ProductDocuments/DataSheets/SAM-D5x-E5x-Family-Data-Sheet-DS60001507.pdf | ||
// https://ww1.microchip.com/downloads/en/DeviceDoc/70005321A.pdf | ||
|
||
#ifndef LED_PIN | ||
#define LED_PIN PIN('C', 18) | ||
#endif | ||
|
||
#ifndef BUTTON_PIN | ||
#define BUTTON_PIN PIN('B', 31) | ||
#endif | ||
|
||
#ifndef UART_DEBUG | ||
#define UART_DEBUG USART1 | ||
#endif | ||
|
||
#pragma once | ||
#include <sam.h> | ||
|
||
#include <stdbool.h> | ||
#include <stdint.h> | ||
#include <stdio.h> | ||
#include <string.h> | ||
|
||
#define BIT(x) (1UL << (x)) | ||
#define SETBITS(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK) | ||
#define PIN(bank, num) ((((bank) - 'A') << 8) | (num)) | ||
#define PINNO(pin) (pin & 255) | ||
#define PINBANK(pin) (pin >> 8) | ||
|
||
static inline void spin(volatile uint32_t count) { | ||
while (count--) (void) 0; | ||
} | ||
|
||
static inline uint32_t clock_sys_freq(void) { | ||
return 48000000U; | ||
} | ||
|
||
enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG }; | ||
enum { GPIO_OTYPE_PUSH_PULL, GPIO_OTYPE_OPEN_DRAIN }; | ||
enum { GPIO_SPEED_LOW, GPIO_SPEED_MEDIUM, GPIO_SPEED_HIGH, GPIO_SPEED_INSANE }; | ||
enum { GPIO_PULL_NONE, GPIO_PULL_UP, GPIO_PULL_DOWN }; | ||
#define GPIO(N) ((port_group_registers_t *) (PORT_BASE_ADDRESS + 0x80 * (N))) | ||
typedef port_group_registers_t GPIO_TypeDef; | ||
static inline GPIO_TypeDef *gpio_bank(uint16_t pin) { | ||
return GPIO(PINBANK(pin)); | ||
} | ||
static inline void gpio_toggle(uint16_t pin) { | ||
gpio_bank(pin)->PORT_OUTTGL = BIT(PINNO(pin)); | ||
} | ||
static inline bool gpio_read(uint16_t pin) { | ||
return gpio_bank(pin)->PORT_IN & BIT(PINNO(pin)); | ||
} | ||
static inline void gpio_write(uint16_t pin, bool val) { | ||
GPIO_TypeDef *gpio = gpio_bank(pin); | ||
if (val) { | ||
gpio->PORT_OUTSET = BIT(PINNO(pin)); | ||
} else { | ||
gpio->PORT_OUTCLR = BIT(PINNO(pin)); | ||
} | ||
} | ||
static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t type, | ||
uint8_t speed, uint8_t pull, uint8_t af) { | ||
(void) type, (void) speed, (void) pull, (void) af; | ||
GPIO_TypeDef *gpio = gpio_bank(pin); | ||
uint32_t mask = BIT(PINNO(pin)); | ||
MCLK_REGS->MCLK_APBBMASK |= MCLK_APBBMASK_PORT_Msk; | ||
if (mode == GPIO_MODE_INPUT) { | ||
gpio->PORT_DIRCLR = mask; | ||
} else { | ||
gpio->PORT_DIRSET = mask; | ||
} | ||
} | ||
static inline void gpio_input(uint16_t pin) { | ||
gpio_init(pin, GPIO_MODE_INPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, | ||
GPIO_PULL_NONE, 0); | ||
} | ||
static inline void gpio_output(uint16_t pin) { | ||
gpio_init(pin, GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, | ||
GPIO_PULL_NONE, 0); | ||
} | ||
|
||
typedef sercom_usart_int_registers_t USART_TypeDef; | ||
#define USART1 ((USART_TypeDef *) SERCOM0_BASE_ADDRESS) | ||
#define USART2 ((USART_TypeDef *) SERCOM1_BASE_ADDRESS) | ||
#define USART3 ((USART_TypeDef *) SERCOM2_BASE_ADDRESS) | ||
static inline bool uart_init(USART_TypeDef *uart, unsigned long baud) { | ||
uint16_t rx = 0, tx = 0; // Pins | ||
uint8_t rx_mux = 0, tx_mux = 0; | ||
if (uart == USART1) { | ||
MCLK_REGS->MCLK_APBAMASK |= MCLK_APBAMASK_SERCOM0_Msk; | ||
GCLK_REGS->GCLK_PCHCTRL[SERCOM0_GCLK_ID_CORE] = | ||
GCLK_PCHCTRL_GEN_GCLK0 | GCLK_PCHCTRL_CHEN_Msk; | ||
GCLK_REGS->GCLK_PCHCTRL[SERCOM0_GCLK_ID_SLOW] = | ||
GCLK_PCHCTRL_GEN_GCLK3 | GCLK_PCHCTRL_CHEN_Msk; | ||
tx = PIN('A', 4), rx = PIN('A', 5); | ||
rx_mux = MUX_PA05D_SERCOM0_PAD1, tx_mux = MUX_PA04D_SERCOM0_PAD0; | ||
} else if (uart == USART2) { | ||
MCLK_REGS->MCLK_APBAMASK |= MCLK_APBAMASK_SERCOM1_Msk; | ||
tx = PIN('C', 27), rx = PIN('C', 28); | ||
} else if (uart == USART3) { | ||
MCLK_REGS->MCLK_APBBMASK |= MCLK_APBBMASK_SERCOM2_Msk; | ||
tx = PIN('A', 9), rx = PIN('A', 8); | ||
} else { | ||
return false; | ||
} | ||
gpio_bank(rx)->PORT_WRCONFIG = | ||
PORT_WRCONFIG_PMUX(rx_mux) | PORT_WRCONFIG_WRPMUX(1) | | ||
PORT_WRCONFIG_PMUXEN(1) | PORT_WRCONFIG_WRPINCFG(1) | BIT(PINNO(rx)); | ||
gpio_bank(tx)->PORT_WRCONFIG = | ||
PORT_WRCONFIG_PMUX(tx_mux) | PORT_WRCONFIG_WRPMUX(1) | | ||
PORT_WRCONFIG_PMUXEN(1) | PORT_WRCONFIG_WRPINCFG(1) | BIT(PINNO(tx)); | ||
uart->SERCOM_CTRLA = SERCOM_USART_INT_CTRLA_DORD(1) | | ||
SERCOM_USART_INT_CTRLA_MODE(1 /* INT_CLK */) | | ||
SERCOM_USART_INT_CTRLA_RXPO(1 /* PAD1 */) | | ||
SERCOM_USART_INT_CTRLA_TXPO(0 /* PAD0 */) | | ||
SERCOM_USART_INT_CTRLA_SAMPR(1); | ||
uart->SERCOM_BAUD = (uint16_t) (clock_sys_freq() / (16 * baud)); | ||
uart->SERCOM_CTRLB = SERCOM_USART_INT_CTRLB_RXEN(1) | | ||
SERCOM_USART_INT_CTRLB_TXEN(1) | | ||
SERCOM_USART_INT_CTRLB_CHSIZE(0); | ||
while (uart->SERCOM_SYNCBUSY & SERCOM_USART_INT_SYNCBUSY_CTRLB_Msk) spin(1); | ||
uart->SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_ENABLE(1); | ||
while (uart->SERCOM_SYNCBUSY & SERCOM_USART_INT_SYNCBUSY_ENABLE_Msk) spin(1); | ||
return true; | ||
} | ||
static inline void uart_write_byte(USART_TypeDef *uart, uint8_t byte) { | ||
while (!(uart->SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_DRE_Msk)) spin(1); | ||
uart->SERCOM_DATA = byte; | ||
} | ||
static inline void uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) { | ||
while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++); | ||
} | ||
static inline bool uart_read_ready(USART_TypeDef *uart) { | ||
return (uart->SERCOM_INTFLAG & SERCOM_USART_EXT_INTFLAG_RXC_Msk); | ||
} | ||
static inline uint8_t uart_read_byte(USART_TypeDef *uart) { | ||
return (uint8_t) (uart->SERCOM_DATA & 255U); | ||
} | ||
|
||
static inline void rng_init(void) { | ||
MCLK_REGS->MCLK_APBCMASK |= MCLK_APBCMASK_TRNG_Msk; | ||
TRNG_REGS->TRNG_CTRLA = TRNG_CTRLA_ENABLE_Msk; | ||
} | ||
static inline uint32_t rng_read(void) { | ||
while ((TRNG_REGS->TRNG_INTFLAG & TRNG_INTFLAG_DATARDY_Msk) == 0) spin(1); | ||
return TRNG_REGS->TRNG_DATA; | ||
} | ||
|
||
#define UUID ((uint8_t *) UID_BASE) // Unique 96-bit chip ID. TRM 39.1 | ||
|
||
// Helper macro for MAC generation | ||
#define GENERATE_LOCALLY_ADMINISTERED_MAC() \ | ||
{ \ | ||
2, UUID[0] ^ UUID[1], UUID[2] ^ UUID[3], UUID[4] ^ UUID[5], \ | ||
UUID[6] ^ UUID[7] ^ UUID[8], UUID[9] ^ UUID[10] ^ UUID[11] \ | ||
} | ||
|
||
static inline bool timer_expired(volatile uint64_t *t, uint64_t prd, | ||
uint64_t now) { | ||
if (now + prd < *t) *t = 0; // Time wrapped? Reset timer | ||
if (*t == 0) *t = now + prd; // Firt poll? Set expiration | ||
if (*t > now) return false; // Not expired yet, return | ||
*t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time | ||
return true; // Expired, return true | ||
} | ||
|
||
static inline void clock_init(void) { | ||
SCB->CPACR |= (15U << 20); // Enable FPU | ||
SysTick_Config(clock_sys_freq() / 1000); // Sys tick every 1ms | ||
} | ||
|
||
static inline void gpio_set_irq_handler(uint16_t pin, void (*fn)(void *), | ||
void *arg) { | ||
(void) pin, (void) fn, (void) arg; | ||
} | ||
|
||
static inline void ethernet_init(void) { | ||
uint16_t pins[] = {PIN('A', 12), PIN('A', 13), PIN('A', 14), PIN('A', 15), | ||
PIN('A', 17), PIN('A', 18), PIN('A', 19), PIN('C', 11), | ||
PIN('C', 12), PIN('C', 20)}; | ||
uint32_t af[] = {MUX_PA12L_GMAC_GRX1, MUX_PA13L_GMAC_GRX0, | ||
MUX_PA14L_GMAC_GTXCK, MUX_PA15L_GMAC_GRXER, | ||
MUX_PA17L_GMAC_GTXEN, MUX_PA18L_GMAC_GTX0, | ||
MUX_PA19L_GMAC_GTX1, MUX_PC11L_GMAC_GMDC, | ||
MUX_PC12L_GMAC_GMDIO, MUX_PC20L_GMAC_GRXDV}; | ||
|
||
MCLK_REGS->MCLK_APBBMASK |= MCLK_APBBMASK_PORT_Msk; | ||
|
||
for (size_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) { | ||
int bank = PINBANK(pins[i]), no = PINNO(pins[i]); | ||
PORT_REGS->GROUP[bank].PORT_PINCFG[no] |= PORT_PINCFG_PMUXEN_Msk; | ||
volatile uint8_t *m = &PORT_REGS->GROUP[bank].PORT_PMUX[no / 2], v = m[0]; | ||
if (no & 1) { | ||
m[0] = (uint8_t) ((v & ~0xf0) | PORT_PMUX_PMUXO(af[i])); | ||
} else { | ||
m[0] = (uint8_t) ((v & ~0x0f) | PORT_PMUX_PMUXE(af[i])); | ||
} | ||
} | ||
|
||
PORT_REGS->GROUP[0].PORT_PINCFG[17] |= PORT_PINCFG_DRVSTR_Msk; | ||
PORT_REGS->GROUP[0].PORT_PINCFG[18] |= PORT_PINCFG_DRVSTR_Msk; | ||
PORT_REGS->GROUP[0].PORT_PINCFG[19] |= PORT_PINCFG_DRVSTR_Msk; | ||
|
||
// Reset PHY | ||
uint16_t phy_pin = PIN('C', 21); | ||
gpio_output(phy_pin); | ||
gpio_write(phy_pin, false); | ||
spin(999); | ||
gpio_write(phy_pin, true); | ||
spin(999); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
ENTRY(Reset_Handler); | ||
MEMORY { | ||
flash(rx) : ORIGIN = 0x00000000, LENGTH = 1024k | ||
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 256k | ||
} | ||
_estack = ORIGIN(sram) + LENGTH(sram); | ||
|
||
SECTIONS { | ||
.vectors : { FILL(256) KEEP(*(.vectors)) } > flash | ||
.text : { *(.text*) } > flash | ||
.rodata : { *(.rodata*) } > flash | ||
|
||
.data : { | ||
_sdata = .; | ||
*(.first_data) | ||
*(.data SORT(.data.*)) | ||
_edata = .; | ||
} > sram AT > flash | ||
_sidata = LOADADDR(.data); | ||
|
||
.bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > sram | ||
|
||
. = ALIGN(8); | ||
_end = .; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// SPDX-FileCopyrightText: 2022-2023 Cesanta Software Limited | ||
// SPDX-License-Identifier: MIT | ||
|
||
#include "hal.h" | ||
#include "mongoose.h" | ||
#include "net.h" | ||
|
||
#define BLINK_PERIOD_MS 500 // LED blinking period in millis | ||
#define LOG_PERIOD_MS 1000 // Info log period in millis | ||
|
||
void SystemInit(void) { // Called automatically by startup code | ||
clock_init(); | ||
rng_init(); | ||
} | ||
|
||
static volatile uint64_t s_ticks; // Milliseconds since boot | ||
void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms | ||
s_ticks++; | ||
} | ||
|
||
uint64_t mg_millis(void) { // Let Mongoose use our uptime function | ||
return s_ticks; // Return number of milliseconds since boot | ||
} | ||
|
||
void mg_random(void *buf, size_t len) { // Use on-board RNG | ||
for (size_t n = 0; n < len; n += sizeof(uint32_t)) { | ||
uint32_t r = rng_read(); | ||
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r)); | ||
} | ||
} | ||
|
||
static void timer_fn(void *arg) { | ||
struct mg_tcpip_if *ifp = arg; // show network stats | ||
const char *names[] = {"down", "up", "req", "ready"}; | ||
MG_INFO(("Ethernet: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u", | ||
names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent, | ||
ifp->ndrop, ifp->nerr)); | ||
} | ||
|
||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { | ||
if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "ok\n"); | ||
(void) ev_data, (void) fn_data; | ||
} | ||
|
||
int main(void) { | ||
gpio_input(BUTTON_PIN); | ||
gpio_output(LED_PIN); | ||
uart_init(UART_DEBUG, 115200); | ||
ethernet_init(); | ||
|
||
MG_INFO(("Starting, CPU freq %g MHz", (double) clock_sys_freq() / 1000000)); | ||
|
||
struct mg_mgr mgr; // Initialise | ||
mg_mgr_init(&mgr); // Mongoose event manager | ||
mg_log_set(MG_LL_DEBUG); // Set log level | ||
|
||
// Initialise Mongoose network stack | ||
struct mg_tcpip_driver_same54_data driver_data = {.mdc_cr = 5}; | ||
struct mg_tcpip_if mif = {.mac = {2, 3, 4, 5, 6, 7}, | ||
// Uncomment below for static configuration: | ||
.ip = mg_htonl(MG_U32(192, 168, 0, 207)), | ||
.mask = mg_htonl(MG_U32(255, 255, 255, 0)), | ||
.gw = mg_htonl(MG_U32(192, 168, 0, 1)), | ||
.driver = &mg_tcpip_driver_same54, | ||
.driver_data = &driver_data}; | ||
mg_tcpip_init(&mgr, &mif); | ||
mg_timer_add(&mgr, LOG_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, &mif); | ||
|
||
MG_INFO(("MAC: %M. Waiting for IP...", mg_print_mac, mif.mac)); | ||
while (mif.state != MG_TCPIP_STATE_READY) { | ||
mg_mgr_poll(&mgr, 0); | ||
} | ||
|
||
MG_INFO(("Initialising application...")); | ||
mg_http_listen(&mgr, "http://0.0.0.0", fn, NULL); | ||
// web_init(&mgr); | ||
|
||
MG_INFO(("Starting event loop")); | ||
for (;;) { | ||
mg_mgr_poll(&mgr, 0); | ||
} | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../mongoose.c |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../mongoose.h |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../device-dashboard/net.c |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../device-dashboard/net.h |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../device-dashboard/packed_fs.c |
Oops, something went wrong.