
Analog keyboard
hackclub/OnBoard
💡 Join 1,000 teenagers and make your first PCB with a $100 grant!

The Pollutag Node2 repo. Contain FW and HW. This LoRaWAN node designed for autonomous work above 10 years
Pollutag node2 is an open-source printed circuit board by WeSpeakEnglish, from the WeSpeakEnglish/pollutag_node2 repository on GitHub, published under the GPL-3.0 licence. It is a 2-layer board of 37.8 × 54 mm with 198 parts on 43 nets, routed with 436 tracks and 57 vias.
Its parts include 12 capacitors, 10 resistors, 4 diodes, 2 transistors and 2 connectors.

The LoraWAN node for air quatity monitoring based on STM32WLE and SEN54 Sensirion sensor 868 Mhz LoRaWAN node for continuous air quality monitoring. The node operates autonomously and support remote commands via downlinks. SEN54 connected via I2C2 interface.
Measurement cycle (default every 30 minutes, set by downlink 02):
| Time | Action |
|---|---|
| 0 s | SEN54 power on (skipped while the battery level byte is 143 or lower) |
| 1 s | Sensor reset, VOC algorithm state restored, measurement started |
| 31-60 s | PM: 30 s start-up, then one sample per second averaged over 30 s (Sensirion "Reduced Power Operation SEN5x" recommendation). VOC, temperature and humidity are taken from the last sample |
| 61 s | Result ready; uplink sent if no uplink period is set |
| 62 s | VOC algorithm state saved, measurement stopped |
| 63 s | SEN54 power off |
The sensor is powered for 63 s per cycle (about 70 mA while measuring). VOC note: the SEN54 VOC index learns its baseline during continuous operation. Because the sensor is powered off between measurements, the VOC value is only a rough, short-term indicator. The VOC algorithm state is saved before power-off and restored at power-up to reduce the effect.
Uplinks are unconfirmed, with ADR on. By default one uplink is sent after each measurement; with
downlink 03 the node sends instead on its own timer (each uplink carries the latest measurement).
If the duty-cycle limit does not allow sending yet, the LoRaWAN stack queues the uplink and sends it
as soon as allowed. Settings made by downlink are not stored: after a reset the defaults apply again.
All commands on Port 2, two bytes [command, value] (except reset):
| Downlink | Command | Value |
|---|---|---|
01 00 / 01 01 | LED / load output off / on | This LED is enhanced with a FET transistor, so you may connect some load to be switched ON/OFF (see the schematic in PCB folder) |
02 XX | Measuring period | XX minutes (hex). 00 = measuring off, 01 is raised to the 2 minute minimum. Default 30 min (02 1E) |
03 XX | Uplink period | XX minutes (hex), minimum 1. Uplinks are then sent only every XX minutes. 00 = one uplink after each measurement (default) |
FF FF FF FF FF FF | Reset the device |
Examples: 02 3C measure every 60 min, 03 05 send every 5 min, 01 01 LED on.
Downlink payload encoder example:
function Encoder(measurements, port) {
// LED on/off
if (measurements.LED_ON !== undefined) return [0x01, measurements.LED_ON.value ? 1 : 0];
// measuring period, minutes (0 = off)
if (measurements.MEASURE_MIN !== undefined) return [0x02, measurements.MEASURE_MIN.value & 0xFF];
// uplink period, minutes (0 = after each measurement)
if (measurements.UPLINK_MIN !== undefined) return [0x03, measurements.UPLINK_MIN.value & 0xFF];
return [];
}The node uplink data packet (Port 2) contains 14 payload bytes with the following data: PM1 concentration, PM2.5 concentration, PM10 concentration (30 s averages), temperature, humidity, volatile organic compounds index (VOC), battery level related to volts, status of LED output.
| Bytes | Value | Encoding | Invalid / no data |
|---|---|---|---|
| 0-1 | PM1.0 | ug/m3 x10, big endian | FF FF |
| 2-3 | PM2.5 | ug/m3 x10, big endian | FF FF |
| 4-5 | PM10 | ug/m3 x10, big endian | FF FF |
| 6-7 | Temperature | byte6 + byte7/100 - 100 [degC] | FF FF |
| 8-9 | Humidity | byte8 + byte9/100 [%RH] | FF FF |
| 10-11 | VOC index | x10, big endian | FF FF |
| 12 | Battery level | x 0.0238 = volts | |
| 13 | LED output | 0 = off, 1 = on |
Invalid values are sent when the sensor did not answer or had no data (for example while the battery is too low to power it).
Decoder for payload on server side (uplink):
function Decoder(bytes, port, uplink_info) {
var decoded = {};
function u16(i) { return bytes[i]*256 + bytes[i+1]; }
function invalid(i) { return bytes[i] === 0xFF && bytes[i+1] === 0xFF; }
decoded.PM1 = invalid(0) ? null : (u16(0)/10.0).toFixed(2);
decoded.PM2_5 = invalid(2) ? null : (u16(2)/10.0).toFixed(2);
decoded.PM10 = invalid(4) ? null : (u16(4)/10.0).toFixed(2);
decoded.T = invalid(6) ? null : (-100.0 + bytes[6] + bytes[7]/100.0).toFixed(2);
decoded.RH = invalid(8) ? null : (bytes[8] + bytes[9]/100).toFixed(2);
decoded.VOC = invalid(10) ? null : (u16(10)/10.0).toFixed(2);
decoded.Battery = (bytes[12]*0.0238).toFixed(2);//battery , volts
decoded.LEDON = bytes[13];
decoded.RSSI = normalizedPayload.gateways[0].rssi.toFixed(0);
decoded.SNR = normalizedPayload.gateways[0].snr.toFixed(0);
decoded.FREQUENCY = normalizedPayload.frequency;
return decoded;
}.ioc was saved with STM32CubeMX 6.15, firmware package STM32Cube FW_WL V1.3.1).FW folder. The project is called pollutag_node2.pollutag_node2 launch configuration (ST-LINK, connect under reset).Notes:
.ioc; building works without it.FW/Core/Src/app_sensor.c (sensor and measurement cycle) and FW/LoRaWAN/App/lora_app.c
(uplink payload, downlinks), inside the CubeMX USER CODE sections so regenerating keeps it.FW/STM32WLE5JCIX_FLASH.ld.FW/LoRaWAN/App/lorawan_credentials.h before building, see LoRaWAN credentials.Originally LORA-E5 (Wio) modules came pre-programmed with AT-command enabled stack, working like a LoRaWAN modem.
To use the core MCU for this node it is needed first to erase the chip and prepare the project for proper comissioning. You may use commissioning identificators already pre-programmed into the chip by Seeed studio, or generate the new via TTN/ Helium console.
To erase chip connect it via ST-LINK under reset (you need to short RESET pin (J1, pin 5) of the MCU to the ground before you press "Connect" button in STM32CubeProgrammer).

After you successfully connected the MCU, it need to be reprogrammed Read protection Option Byte to AA (no protection)

After completing this process, the chip should be successfully cleaned.

The keys are not taken from the .ioc (it keeps zeros). Each device has its own small file
FW/LoRaWAN/App/lorawan_credentials.h, which overrides the CubeMX values and survives code regeneration:
#define LORAWAN_DEVICE_EUI 00,00,00,00,00,00,00,00 // 8 bytes
#define LORAWAN_JOIN_EUI 00,00,00,00,00,00,00,00 // 8 bytes (AppEUI)
#define LORAWAN_APP_KEY 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 // 16 bytesFW/LoRaWAN/App/lorawan_credentials_template.h to FW/LoRaWAN/App/lorawan_credentials.h
(if the file is missing the build stops with a message saying so).DevEUI and JoinEUI it uses on the debug UART (115200 baud).Options:
LORAWAN_DEVICE_EUI all 00: the DevEUI is derived from the MCU unique ID. Read it from the boot log
and register the device with that DevEUI in the network console.LORAWAN_JOIN_EUI may be left all 00 for networks that accept it (useful if you plan to move to a
different LoRaWAN network later: register the node on the new network console, reset the node, then
delete it on the old console and reset again).lorawan_credentials.h is device specific and secret: do not share or commit it (it is listed in
FW/.gitignore); share lorawan_credentials_template.h instead.
Hackaday project: LINK
OSHWLab STD project: LINK
OSHWLab PRO poject: LINK
Pollutants.eu: LINK
| Qty | Value | Footprint | References |
|---|---|---|---|
| 27 | PollutagNo-easyedapro:S1751-46R | 3V3, BAT-, BAT+, DONE, GND, LOAD, PA0, PA2, PA3, PA10, PA15, PB0, PB10, PB13, PB14, PB15 +11 | |
| 12 | PollutagNo-easyedapro:CAP0603 | C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12 | |
| 10 | PollutagNo-easyedapro:R0603 | R1, R2, R4, R5, R6, R7, R8, R9, R10, R11 | |
| 2 | PollutagNo-easyedapro:LEDC2012X90N | LED1, LED2 | |
| 2 | PollutagNo-easyedapro:SOT-23-3_L2.9-W1.6-P1.90-LS2.8-BR | Q1, Q2 | |
| 2 | PollutagNo-easyedapro:IND-SMD_L3.0-W3.0_SRN3015 | L1, L2 | |
| 2 | PollutagNo-easyedapro:SOD-123_L2.6-W1.7-LS3.5-RD | D1, D2 | |
| 1 | Pad_e59 | ||
| 1 | Pad_e164 | ||
| 1 | Pad_e99 | ||
| 1 | Pad_e107 | ||
| 1 | Pad_e116 | ||
| 1 | Pad_e69 | ||
| 1 | Pad_e92 | ||
| 1 | Pad_e122 | ||
| 1 | Pad_e183 | ||
| 1 | Pad_e79 | ||
| 1 | Pad_e159 | ||
| 1 | Pad_e143 | ||
| 1 | Pad_e86 | ||
| 1 | Pad_e182 | ||
| 1 | Pad_e94 | ||
| 1 | Pad_e115 | ||
| 1 | Pad_e108 | ||
| 1 | Pad_e158 | ||
| 1 | Pad_e161 | ||
| 1 | Pad_e167 | ||
| 1 | Pad_e173 | ||
| 1 | Pad_e101 | ||
| 1 | Pad_e104 | ||
| 1 | Pad_e136 | ||
| 1 | Pad_e190 | ||
| 1 | Pad_e95 | ||
| 1 | Pad_e160 | ||
| 1 | Pad_e102 | ||
| 1 | Pad_e100 | ||
| 1 | Pad_e186 | ||
| 1 | Pad_e77 | ||
| 1 | Pad_e129 | ||
| 1 | Pad_e124 | ||
| 1 | Pad_e172 | ||
| 1 | PollutagNo-easyedapro:SM06B-GHS-TB | J3 | |
| 1 | Pad_e62 | ||
| 1 | Pad_e103 | ||
| 1 | Pad_e175 | ||
| 1 | PollutagNo-easyedapro:142-0701-801 | J2 | |
| 1 | Pad_e163 | ||
| 1 | Pad_e137 | ||
| 1 | Pad_e177 | ||
| 1 | Pad_e134 | ||
| 1 | Pad_e133 | ||
| 1 | Pad_e91 | ||
| 1 | Pad_e117 | ||
| 1 | Pad_e126 | ||
| 1 | Pad_e68 | ||
| 1 | Pad_e142 | ||
| 1 | Pad_e65 | ||
| 1 | Pad_e141 | ||
| 1 | Pad_e148 | ||
| 1 | Pad_e170 | ||
| 1 | Pad_e74 | ||
| 1 | Pad_e73 | ||
| 1 | Pad_e165 | ||
| 1 | Pad_e166 | ||
| 1 | Pad_e138 | ||
| 1 | Pad_e80 | ||
| 1 | Pad_e106 | ||
| 1 | Pad_e87 | ||
| 1 | Pad_e187 | ||
| 1 | Pad_e121 | ||
| 1 | Pad_e181 | ||
| 1 | Pad_e188 | ||
| 1 | Pad_e109 | ||
| 1 | Pad_e112 | ||
| 1 | Pad_e125 | ||
| 1 | Pad_e96 | ||
| 1 | Pad_e179 | ||
| 1 | Pad_e66 | ||
| 1 | Pad_e110 | ||
| 1 | Pad_e185 |
The 130 most common of 198 parts.

hackclub/OnBoard
💡 Join 1,000 teenagers and make your first PCB with a $100 grant!

pcbreflux/espressif
all espressif stuff will committed here

rogerclarkmelbourne/STM32_Pill_Boards
Various STM32 boards in a form factor similar to the Maple Mini or Blue Pill board

modern-hobbyist/aesir
Collection of KiCad PCB designs for custom keyboards, including full-sized and split ergonomic layouts with STM32F072CBT6 microcontrollers.

ageppert/Core-64-Interactive-Core-Memory-Badge
64 bits of core memory and LEDs to interact with directly using a magnetic wand.

AcheronProject/Doddle60
Doddle60 is a 60% ISO layout PCB with a minimal layout number support intended for ISO users.

ja1umi/MUTIF09Project
Various design materials for building my 8-bit-microprocessor-based single board computer(s)

ericrlau/SP56
Bluetooth low engery keyboard. Layout based on Sofle keyboard with some modifications. NRF52840 powered. Designed around Kaihl Choc switches and spacing.
It measures 37.8 × 54 mm and has 2 copper layers, 1.6 mm thick.
198 parts. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.
From the WeSpeakEnglish/pollutag_node2 repository on GitHub, at the commit this page was made from: the board is PCB/kicad/pollutag_node2.kicad_pcb and its schematic PCB/kicad/pollutag_node2.kicad_sch. PCBKit opens the board in the browser, with nothing to install.
Its repository publishes it under the GPL-3.0 licence: you may reuse it on that licence's terms (keep the author's notice, and for a copyleft licence share your changes alike).