Files
stm32samples/F1:F103/AS3935-lightning/Readme.md
Edward Emelianov f74ea14bbe add documentation
2026-04-25 20:46:52 +03:00

13 KiB
Raw Blame History

AS3935-based Lightning Detector

Overview

This project implements a lightning detection system using two AS3935 Franklin Lightning Sensors interfaced with an STM32F103 microcontroller. The device communicates over USB CDC (virtual serial port) and exposes a text-based command protocol for configuration, monitoring, and data extraction. In the next version it is possible to add RS-232 or RS-485 as an alternative to USB.

Key features:

  • Dual AS3935 sensors on single SPI channel.
  • On-chip ADC for internal MCU temperature and voltage supply monitoring (more features may be added in the future).
  • Persistent configuration storage in internal Flash memory.
  • USB CDC interface with automatic detection of lightning events.
  • Extensive command set for real-time tuning and status readout.

Hardware Connections

Pin Configuration Function
PA0 GPIO INT0 - External interrupt from sensor 0 (pull-down)
PA1 GPIO INT1 - External interrupt from sensor 1 (pull-down)
PA2 GPIO CS0 - SPI chip select for sensor 0
PA3 GPIO CS1 - SPI chip select for sensor 1
PA5 SPI1 SCK SPI clock
PA6 SPI1 MISO SPI Master In / Slave Out
PA7 SPI1 MOSI SPI Master Out / Slave In
PA9 USART1 Tx (unused in this version)
PA10 USART1 Rx (unused)
PA15 GPIO USB D+ pull-up control (1.5 kΩ)
PC13 GPIO On-board LED (active low)

The two AS3935 sensors share the SPI bus but have separate chip select lines and interrupt pins. If necessary, it can be expanded to include more sensors.


USB CDC Communication

The device appears as a virtual COM port when connected to a host computer. Communication parameters (baud rate, parity, etc.) are ignored — the underlying USB bulk transport guarantees reliable delivery.

  • Line coding: default sent by host (e.g., 115200 8N1) is accepted but not used.
  • Ring buffers: 1024 bytes each for TX and RX.
  • Command terminator: every command must end with a newline (\n). The response is also terminated by a newline, except for multi-line outputs (help, dumpconf, SPI).

When the host opens the virtual COM port (DTR asserted), the device becomes ready. If the port is closed, the CDCready flag is cleared and any outgoing data is discarded.


Command Protocol

General Syntax

<command> [<channel>] [ = <value> ]
  • command — case-sensitive name (exactly as listed, all lowercase).
  • channel — numeric index of the sensor (0 or 1). Required for sensor-specific commands; omitted for global commands.
  • value — decimal integer, hexadecimal (0x prefix), binary (0b prefix), or a string (for commands like setiface). For the SPI command, the value is a sequence of hex bytes and/or quoted text.
  • whitespaces around = and between tokens is ignored.

There are two modes:

  1. Getter — if = is absent, the current value is returned:

    command<channel> = <current_value>
    

    No OK response follows.

  2. Command — also don't need =, but returns OK or error code.

  3. Setter — if = is present, the value after = is assigned. The returned value is text describing error code (OK etc.). A getter-style response is not returned after a successful set; only OK appears.

For channel-dependent commands the response includes the channel number, e.g.:

gain0 = 5

Global commands (no channel) use the same format without a number:

restonstart = 1

Commands that are purely actions (e.g., clearstat, resetdef) only take a channel number and do not accept an = sign. On success they reply OK.

Error Codes

If a command cannot be executed, one of the following error strings is returned instead of OK:

Error String Meaning
BADCMD Unknown command
BADPAR Invalid channel number
BADVAL Invalid value for the setter (out of range or malformed)
WRONGLEN Buffer overflow or line too long
CANTRUN SPI communication failure, sensor not responding, or illegal state
BUSY SPI or ring buffer busy - retry later
OVERFLOW Input string exceeded maximum length (256 characters)

Command Reference

All commands are defined in the source file commproto.cpp using macro COMMAND_TABLE.

Sensor Configuration Commands

These commands read or write parameters of a specific AS3935 sensor. They require the channel number as the first argument. For details, please refer to the sensor's technical documentation.

Command Description Value Range Default
displco Display on IRQ pin: 0=nothing, 1=TRCO, 2=SRCO, 3=LCO 0 - 3 0
gain Amplifier gain (AFE_GB) 0 - 31 18
lco_fdiv Antenna LCO frequency divider 0 - 3 0
maskdist Mask disturbers (1 = mask, 0 = allow) 0 or 1 0
minnumlig Minimum lightning number (0→1, 1→5, 2→9, 3→16) 0 - 3 0
nflev Noise floor level 0 - 7 2
srej Spike rejection 0 - 15 2
tuncap Tune capacitor setting (n·8 pF) 0 - 15 0
wdthres Watchdog threshold 0 - 15 2

Syntax examples:

gain 0 = 12          set gain of sensor 0 to 12
gain 1               read gain of sensor 1 → gain1 = 5
displco 0 = 1        configure sensor 0 IRQ pin to output TRCO clock
displco 0            read display mode → displco0 = 1

Sensor Action & Status Commands

These commands do not take a value after =, only a channel number.

Command Description
clearstat Clear the lightning statistics (last 15 min)
distance Read estimated distance to the lightning in km (0-63, 63=out of range)
energy Read energy of the last lightning (24-bit value)
intcode Read the last interrupt code: 1=Noise, 4=Disturber, 8=Lightning
iscalib Check if both RCO and TRCO calibrations are done (returns 1 if done)
resetdef Reset the sensor to factory default settings
wakeup Wake up the sensor and run RCO calibration

Examples:

wakeup 0            wake up sensor 0, auto-calibrate → OK
iscalib 0           read calibration status → iscalib0 = 1
energy 0            read energy → energy0 = 123456
distance 1          read distance → distance1 = 12
intcode 0           read interrupt flags → intcode0 = 8
clearstat 0         clear statistics → OK
resetdef 0          reset to defaults → OK

Global (Non-Sensor) Commands

These commands do not require a channel number.

Command Description
dumpconf Dump the full current configuration (stored in RAM until saved)
eraseflash Erase the entire Flash configuration storage
help Print list of all commands and a repository URL
mcureset Software reset of the MCU
mcutemp MCU internal temperature in tenths of °C (e.g., 287 = 28.7℃)
readconf Read sensor configuration from the chip and update RAM for given channel
restonstart Restore sensor parameters at start-up (0 or 1)
saveconf Save the current RAM configuration to Flash
setiface Get/set the USB interface name string (max 16 characters)
time Current uptime in milliseconds
vdd Supply voltage VDD in hundredths of a volt (e.g., 330 = 3.30V)

Examples:

mcutemp               → mcutemp = 287
vdd                   → vdd = 330
setiface = MyDetector   set interface name to "MyDetector"
setiface              → setiface = MyDetector
restonstart = 1         enable automatic restore at power-up
saveconf                save current configuration to Flash
dumpconf                print all stored and current parameters
readconf 0              read sensor 0 parameters into RAM (for later save)

SPI Low-Level Command

The SPI command allows arbitrary bidirectional SPI transactions to a selected sensor.

Syntax:

SPI <channel> = <hexdata>

<hexdata> is a sequence of bytes expressed as space- or comma-separated hexadecimal numbers (without 0x prefix), optionally with quoted text strings. Anything inside double quotes is transmitted as raw ASCII.

Example:

SPI 0 = 00 01 "hello" 02

This sends the bytes 0x00, 0x01, 'h','e','l','l','o', 0x02 to sensor 0 and returns the received bytes as a hex dump.

Response format:

SPI0 = 
00 01 68 65 6c 6c 6f 02

Lightning Interrupts

The main loop continuously monitors the INT pins (PA0, PA1). When a rising edge is detected and the corresponding DISPLCO setting is 0 (not used as clock output), the code reads the interrupt register:

  • Disturber and noise events are reported as:

    INTERRUPT0=NOICE,DISTURBER
    
  • A lightning event additionally triggers lightning_info(), which prints the energy and distance:

    INTERRUPT0=LIGHTNING
    energy0 = 123456
    distance0 = 12
    

If the pin is configured as a clock output (displco != 0), no interrupt processing occurs. You can mask disturber interrupts by command maskdist n = 0, in this case only NOICE and LIGHTNING will be monitored.


Configuration Storage

User settings are stored in the internal Flash of the STM32F103. The Flash area reserved for configuration starts at a fixed address (defined in the linker script) and has a limited capacity. The structure user_conf contains:

  • userconf_sz — magic cookie (size of the structure).
  • iInterface + iIlength — USB interface name (stored as two bytes per character for Unicode).
  • spars[] — per-sensor parameters (gain, WDTH, NF_LEV, SREJ, MIN_NUM_LIG, MASK_DIST, LCO_FDIV, TUN_CAP).
  • flags.restore — if 1, all parameters are automatically applied after reset.

Mechanism:

  • Each time saveconf is issued, a new copy of the configuration is appended to the Flash storage. A binary search at boot finds the last valid entry.
  • If the storage becomes full, the next saveconf will first erase the entire storage and then write the current configuration.
  • eraseflash erases the whole configuration area; restonstart controls whether the stored configuration is uploaded to sensors at start-up.

Capacity:

Approximately (FLASH_SIZE * 1024 - offset) / sizeof(user_conf) configurations can be stored. For a typical 64 KB device, this is hundreds of entries.


ADC and Internal Sensors

The STM32s ADC continuously samples the internal temperature sensor (channel 16) and the internal reference voltage (channel 17) in scan mode with DMA circular buffer. A median filter of 9 samples is applied to each channel.

  • mcutemp returns temperature in tenths of °C, computed as:

    T = 25 + (V_25 - Vsense)/Avg_Slope, with V_25=1.45V and Avg_Slope=4.3mV/°C.

  • vdd calculates the supply voltage using the internal 1.2V reference:

    Vdd = 1.2 * 4096 / (ADC reading of Vrefint).

These commands do not require a sensor channel.


Examples

Typical Workflow

  1. Connect the device to a USB port and open a terminal at any baud rate.

  2. Type help to see the available commands.

  3. Wake up the sensors and calibrate:

    wakeup 0
    wakeup 1
    
  4. Configure gains, thresholds and other values if need:

    gain 0 = 10
    wdthres 1 = 3
    
  5. Verify settings:

    dumpconf
    
  6. Enable persistent configuration:

    restonstart = 1
    saveconf
    
  7. Monitor lightning events — the device will print INTERRUPT... messages automatically. If need, you can clear statistics periodically or call other setters/getters; for example, reduce the sensitivity or lightning strike threshold during a severe thunderstorm and restore these values after it ends

Reading Sensor Status

Any time you can read sensor status (to get noice information or other data):

intcode 0
energy 0
distance 0

Saving and Restoring Configuration

dumpconf        # see current settings
saveconf        # write to flash
mcureset        # reboot (only if something goes wrong)

After reset, the configuration is automatically loaded (if restonstart was 1).


How to distinguish between identical device

The device uses standard USB VID/PID, which are shared for free use by ST. Therefore, it may be difficult to determine which /dev/ttyACMx this particular device corresponds to.

You can set custom interface name (setiface=...). After storing in flash, reconnect or reset device and run lsusb -v for given device. At iInterface field you will see stored name. This field can be used to create human-readable symlinks in /dev.

For automatical creation of symlink in /dev to your /dev/ttyACMx, add this udev-script to /etc/udev/rules.d/99-usbids.rules

ACTION=="add", DRIVERS=="usb", ENV{USB_IDS}="%s{idVendor}:%s{idProduct}"
ACTION=="add", ENV{USB_IDS}=="0483:5740", ATTRS{interface}=="?*", PROGRAM="/bin/bash -c \"ls /dev | grep $attr{interface} | wc -l \"", SYMLINK+="$attr{interface}%c", MODE="0666", GROUP="tty"

Build Information

Typing help you will see project repository URL, build number and build date at first string.

The code is distributed under GPLv3.