Skip to content

Commit 1d0e25f

Browse files
authored
Arduino_core_STM32 support (improved) (#1571)
* Arduino_core_STM32 support v1 after three previous attempts failed or were abandoned (#1422, #1437, #1486) * fixed compile warning on redefined (v)snprintf * add missing WDG reset, harden VREF and ATEMP to fail gracefully when not available
1 parent 43d4a14 commit 1d0e25f

File tree

7 files changed

+930
-3
lines changed

7 files changed

+930
-3
lines changed

MySensors.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@
7373
#elif defined(ARDUINO_ARCH_STM32F1)
7474
#include "hal/architecture/STM32F1/MyHwSTM32F1.cpp"
7575
#include "hal/crypto/generic/MyCryptoGeneric.cpp"
76+
#elif defined(ARDUINO_ARCH_STM32)
77+
#include "hal/architecture/STM32/MyHwSTM32.cpp"
78+
#include "hal/crypto/generic/MyCryptoGeneric.cpp"
7679
#elif defined(ARDUINO_ARCH_NRF5) || defined(ARDUINO_ARCH_NRF52)
7780
#include "hal/architecture/NRF5/MyHwNRF5.cpp"
7881
#include "hal/crypto/generic/MyCryptoGeneric.cpp"
@@ -336,7 +339,7 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs
336339
#define MY_RAM_ROUTING_TABLE_ENABLED
337340
#elif defined(MY_RAM_ROUTING_TABLE_FEATURE) && defined(MY_REPEATER_FEATURE)
338341
// activate feature based on architecture
339-
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_NRF5) || defined(ARDUINO_ARCH_STM32F1) || defined(TEENSYDUINO) || defined(__linux__) || defined(__ASR6501__) || defined (__ASR6502__)
342+
#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_NRF5) || defined(ARDUINO_ARCH_STM32F1) || defined(ARDUINO_ARCH_STM32) || defined(TEENSYDUINO) || defined(__linux__) || defined(__ASR6501__) || defined (__ASR6502__)
340343
#define MY_RAM_ROUTING_TABLE_ENABLED
341344
#elif defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)
342345
#if defined(__avr_atmega1280__) || defined(__avr_atmega1284__) || defined(__avr_atmega2560__) || defined(__avr_attiny3224__) || defined(__avr_attiny3227__)
@@ -474,6 +477,8 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs
474477
#include "hal/architecture/Linux/MyMainLinuxGeneric.cpp"
475478
#elif defined(ARDUINO_ARCH_STM32F1)
476479
#include "hal/architecture/STM32F1/MyMainSTM32F1.cpp"
480+
#elif defined(ARDUINO_ARCH_STM32)
481+
#include "hal/architecture/STM32/MyMainSTM32.cpp"
477482
#elif defined(__ASR6501__) || defined(__ASR6502__)
478483
#include "hal/architecture/ASR650x/MyMainASR650x.cpp"
479484
#elif defined(__arm__) && defined(TEENSYDUINO)
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
/*
2+
* The MySensors Arduino library handles the wireless radio link and protocol
3+
* between your home built sensors/actuators and HA controller of choice.
4+
* The sensors forms a self healing radio network with optional repeaters. Each
5+
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
6+
* network topology allowing messages to be routed to nodes.
7+
*
8+
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
9+
* Copyright (C) 2013-2025 Sensnology AB
10+
* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
11+
*
12+
* Documentation: http://www.mysensors.org
13+
* Support Forum: http://forum.mysensors.org
14+
*
15+
* This program is free software; you can redistribute it and/or
16+
* modify it under the terms of the GNU General Public License
17+
* version 2 as published by the Free Software Foundation.
18+
*/
19+
20+
/**
21+
* @file MyHwSTM32.cpp
22+
* @brief Hardware abstraction layer for STM32 microcontrollers using STM32duino core
23+
*
24+
* This implementation uses the official STM32duino Arduino core which provides
25+
* STM32Cube HAL underneath. It supports a wide range of STM32 families including
26+
* F0, F1, F4, L0, L4, G0, G4, H7, and more.
27+
*
28+
* Tested on:
29+
* - STM32F401CC/CE Black Pill
30+
* - STM32F411CE Black Pill
31+
*
32+
* Pin Mapping Example (STM32F4 Black Pill):
33+
*
34+
* nRF24L01+ Radio (SPI1):
35+
* - SCK: PA5
36+
* - MISO: PA6
37+
* - MOSI: PA7
38+
* - CSN: PA4
39+
* - CE: PB0 (configurable via MY_RF24_CE_PIN)
40+
*
41+
* RFM69/RFM95 Radio (SPI1):
42+
* - SCK: PA5
43+
* - MISO: PA6
44+
* - MOSI: PA7
45+
* - CS: PA4
46+
* - IRQ: PA3 (configurable)
47+
* - RST: PA2 (configurable)
48+
*/
49+
50+
#include "MyHwSTM32.h"
51+
52+
bool hwInit(void)
53+
{
54+
#if !defined(MY_DISABLED_SERIAL)
55+
MY_SERIALDEVICE.begin(MY_BAUD_RATE);
56+
#if defined(MY_GATEWAY_SERIAL)
57+
// Wait for serial port to connect (needed for native USB)
58+
while (!MY_SERIALDEVICE) {
59+
; // Wait for serial port connection
60+
}
61+
#endif
62+
#endif
63+
64+
// STM32duino EEPROM library auto-initializes on first use
65+
// No explicit initialization required
66+
return true;
67+
}
68+
69+
void hwReadConfigBlock(void *buf, void *addr, size_t length)
70+
{
71+
uint8_t *dst = static_cast<uint8_t *>(buf);
72+
int pos = reinterpret_cast<int>(addr);
73+
74+
for (size_t i = 0; i < length; i++) {
75+
dst[i] = EEPROM.read(pos + i);
76+
}
77+
}
78+
79+
void hwWriteConfigBlock(void *buf, void *addr, size_t length)
80+
{
81+
uint8_t *src = static_cast<uint8_t *>(buf);
82+
int pos = reinterpret_cast<int>(addr);
83+
84+
for (size_t i = 0; i < length; i++) {
85+
EEPROM.update(pos + i, src[i]);
86+
}
87+
88+
// Commit changes to flash (STM32duino EEPROM emulation)
89+
// Note: This happens automatically on next read or explicit commit
90+
}
91+
92+
uint8_t hwReadConfig(const int addr)
93+
{
94+
return EEPROM.read(addr);
95+
}
96+
97+
void hwWriteConfig(const int addr, uint8_t value)
98+
{
99+
EEPROM.update(addr, value);
100+
}
101+
102+
void hwWatchdogReset(void)
103+
{
104+
#if defined(HAL_IWDG_MODULE_ENABLED) && defined(IWDG)
105+
// Reset independent watchdog if enabled
106+
// Use direct register write to reload watchdog counter
107+
// This works whether IWDG was initialized by HAL or LL drivers
108+
IWDG->KR = IWDG_KEY_RELOAD;
109+
#endif
110+
// No-op if watchdog not enabled
111+
}
112+
113+
void hwReboot(void)
114+
{
115+
NVIC_SystemReset();
116+
}
117+
118+
void hwRandomNumberInit(void)
119+
{
120+
// Use internal temperature sensor and ADC noise as entropy source
121+
// This provides reasonably good random seed values
122+
123+
#ifdef ADC1
124+
uint32_t seed = 0;
125+
126+
// Read multiple samples from different sources for entropy
127+
for (uint8_t i = 0; i < 32; i++) {
128+
uint32_t value = 0;
129+
130+
#ifdef TEMP_SENSOR_AVAILABLE
131+
// Try to read internal temperature sensor if available
132+
value ^= analogRead(ATEMP);
133+
#endif
134+
135+
#ifdef VREF_AVAILABLE
136+
// Mix in internal voltage reference reading
137+
value ^= analogRead(AVREF);
138+
#endif
139+
140+
// Mix in current time
141+
value ^= hwMillis();
142+
143+
// Mix in system tick
144+
value ^= micros();
145+
146+
// Accumulate into seed
147+
seed ^= (value & 0x7) << (i % 29);
148+
149+
// Small delay to ensure values change
150+
delayMicroseconds(100);
151+
}
152+
153+
randomSeed(seed);
154+
#else
155+
// Fallback: use millis as weak entropy source
156+
randomSeed(hwMillis());
157+
#endif
158+
}
159+
160+
bool hwUniqueID(unique_id_t *uniqueID)
161+
{
162+
#ifdef UID_BASE
163+
// STM32 unique device ID is stored at a fixed address
164+
// Length is 96 bits (12 bytes) but we store 16 bytes for compatibility
165+
166+
uint32_t *id = (uint32_t *)UID_BASE;
167+
uint8_t *dst = (uint8_t *)uniqueID;
168+
169+
// Copy 12 bytes of unique ID
170+
for (uint8_t i = 0; i < 12; i++) {
171+
dst[i] = ((uint8_t *)id)[i];
172+
}
173+
174+
// Pad remaining bytes with zeros
175+
for (uint8_t i = 12; i < 16; i++) {
176+
dst[i] = 0;
177+
}
178+
179+
return true;
180+
#else
181+
// Unique ID not available on this variant
182+
return false;
183+
#endif
184+
}
185+
186+
uint16_t hwCPUVoltage(void)
187+
{
188+
#if defined(VREF_AVAILABLE) && defined(AVREF) && defined(__HAL_RCC_ADC1_CLK_ENABLE)
189+
// Read internal voltage reference to calculate VDD
190+
// VREFINT is typically 1.2V (varies by STM32 family)
191+
192+
uint32_t vrefint = analogRead(AVREF);
193+
194+
if (vrefint > 0) {
195+
// Calculate VDD in millivolts
196+
// Formula: VDD = 3.3V * 4096 / ADC_reading
197+
// Adjusted: VDD = 1200mV * 4096 / vrefint_reading
198+
return (uint16_t)((1200UL * 4096UL) / vrefint);
199+
}
200+
#endif
201+
202+
// Return typical 3.3V if measurement not available
203+
return 3300;
204+
}
205+
206+
uint16_t hwCPUFrequency(void)
207+
{
208+
// Return CPU frequency in 0.1 MHz units
209+
// F_CPU is defined by the build system (e.g., 84000000 for 84 MHz)
210+
return F_CPU / 100000UL;
211+
}
212+
213+
int8_t hwCPUTemperature(void)
214+
{
215+
#if defined(TEMP_SENSOR_AVAILABLE) && defined(ATEMP) && defined(__HAL_RCC_ADC1_CLK_ENABLE)
216+
// Read internal temperature sensor
217+
// Note: Requires calibration values for accurate results
218+
219+
int32_t temp_raw = analogRead(ATEMP);
220+
221+
#ifdef TEMP110_CAL_ADDR
222+
// Use factory calibration if available (STM32F4, L4, etc.)
223+
uint16_t *temp30_cal = (uint16_t *)TEMP30_CAL_ADDR;
224+
uint16_t *temp110_cal = (uint16_t *)TEMP110_CAL_ADDR;
225+
226+
if (temp30_cal && temp110_cal && *temp110_cal != *temp30_cal) {
227+
// Calculate temperature using two-point calibration
228+
// Formula: T = ((110-30) / (CAL_110 - CAL_30)) * (raw - CAL_30) + 30
229+
int32_t temp = 30 + ((110 - 30) * (temp_raw - *temp30_cal)) /
230+
(*temp110_cal - *temp30_cal);
231+
232+
// Apply user calibration
233+
temp = (temp - MY_STM32_TEMPERATURE_OFFSET) / MY_STM32_TEMPERATURE_GAIN;
234+
235+
return (int8_t)temp;
236+
}
237+
#endif
238+
239+
// Fallback: use typical values (less accurate)
240+
// Typical slope: 2.5 mV/°C, V25 = 0.76V for STM32F4
241+
// This is a rough approximation
242+
float voltage = (temp_raw * 3.3f) / 4096.0f;
243+
int32_t temp = 25 + (int32_t)((voltage - 0.76f) / 0.0025f);
244+
245+
return (int8_t)((temp - MY_STM32_TEMPERATURE_OFFSET) / MY_STM32_TEMPERATURE_GAIN);
246+
#else
247+
// Temperature sensor not available
248+
return FUNCTION_NOT_SUPPORTED;
249+
#endif
250+
}
251+
252+
uint16_t hwFreeMem(void)
253+
{
254+
// Calculate free heap memory
255+
// This uses newlib's mallinfo if available
256+
257+
#ifdef STACK_TOP
258+
extern char *__brkval;
259+
extern char __heap_start;
260+
261+
char *heap_end = __brkval ? __brkval : &__heap_start;
262+
char stack_var;
263+
264+
// Calculate space between heap and stack
265+
return (uint16_t)(&stack_var - heap_end);
266+
#else
267+
// Alternative method: try to allocate and measure
268+
// Not implemented to avoid fragmentation
269+
return FUNCTION_NOT_SUPPORTED;
270+
#endif
271+
}
272+
273+
int8_t hwSleep(uint32_t ms)
274+
{
275+
// TODO: Implement low-power sleep mode
276+
// For now, use simple delay
277+
// Future: Use STM32 STOP or STANDBY mode with RTC wakeup
278+
279+
(void)ms;
280+
return MY_SLEEP_NOT_POSSIBLE;
281+
}
282+
283+
int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms)
284+
{
285+
// TODO: Implement interrupt-based sleep
286+
// Future: Configure EXTI and enter STOP mode
287+
288+
(void)interrupt;
289+
(void)mode;
290+
(void)ms;
291+
return MY_SLEEP_NOT_POSSIBLE;
292+
}
293+
294+
int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1,
295+
const uint8_t interrupt2, const uint8_t mode2, uint32_t ms)
296+
{
297+
// TODO: Implement dual-interrupt sleep
298+
299+
(void)interrupt1;
300+
(void)mode1;
301+
(void)interrupt2;
302+
(void)mode2;
303+
(void)ms;
304+
return MY_SLEEP_NOT_POSSIBLE;
305+
}

0 commit comments

Comments
 (0)