Skip to content

Nour3000/robot-iot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 

Repository files navigation

Robot IoT — Bare-Metal STM32F407 + Cloud Supervision

A 4-wheel, Bluetooth-controlled robot built entirely bare-metal on an STM32F407 — every peripheral driven through direct register access, no HAL, no generated code. Motor PWM, Bluetooth command parsing, multi-channel ADC sensing over DMA, an I²C LCD, and a full cloud supervision layer (ThingSpeak, MQTT, Node-RED dashboard).

Team project (ENSIT). This repository covers my contribution: the complete bare-metal firmware and the cloud supervision stack (ThingSpeak, Mosquitto, Node-RED). The ESP32 AT-command layer was developed by a teammate.

(Bilingual — English first, version française plus bas.)


Architecture

Robot IoT system architecture


See it in action

Node-RED dashboard — live sensor gauges

Node-RED dashboard with three ADC gauges

ThingSpeak — cloud history of the three sensor channels

ThingSpeak charts of the three ADC fields

Serial trace (HTerm) — AT / MQTT traffic during operation

HTerm capture of AT and MQTT commands


What it does

A phone sends text commands (AVANCE, ARRIERE, GAUCHE, DROITE, STOP) over Bluetooth. The STM32 parses them and drives four PWM channels to steer a 4-wheel chassis, ramping the speed up on each successive movement command. In parallel, three analog sensors are sampled continuously through ADC + DMA, shown on a local I²C LCD, echoed back to the phone, and pushed to the cloud every 30 seconds — alternating between ThingSpeak and a local MQTT broker. A Node-RED dashboard subscribes to the broker and displays the values as live gauges.

Everything runs from a single bare-metal main.c: no HAL, no CubeMX-generated code, just CMSIS register access.


The bare-metal approach

This is the part I care about most. Every peripheral is configured by writing directly to its registers — clocks, alternate functions, timers, DMA streams, interrupt priorities. For example, the motor PWM is set up by hand:

void init_tim3_pwm(void)
{
    RCC->APB1ENR |= (1 << 1);
    TIM3->PSC     = 15;                          // 16 MHz / 16 = 1 MHz timer clock
    TIM3->ARR     = 999;                         // → 1 kHz PWM
    TIM3->CCMR1  |= (6 << 4) | (6 << 12);        // PWM mode 1 on CH1/CH2
    TIM3->CCMR2  |= (6 << 4) | (6 << 12);        // PWM mode 1 on CH3/CH4
    TIM3->CCER   |= (1 << 0) | (1 << 4) | (1 << 8) | (1 << 12);
    TIM3->CR1    |= (1 << 0);                     // enable counter
}

No abstraction layer hides what the hardware is doing — which makes the code compact, fast, and a genuine demonstration of how the MCU works underneath the HAL.

The MCU runs on its default HSI 16 MHz clock (no PLL setup), which is plenty for this application and keeps the timing math simple.


Hardware

  • STM32F407VG-DISC1 — ARM Cortex-M4, running on the internal 16 MHz HSI oscillator
  • HC-06 Bluetooth module — wireless command link from a phone
  • H-bridge motor driver + 4-wheel chassis with DC motors
  • 3 analog sensors read on ADC channels (e.g. light / IR / potentiometer)
  • 16×2 I²C LCD (PCF8574 backpack, address 0x27) for local status
  • ESP32 (AT firmware) acting as the Wi-Fi gateway to the cloud

Peripheral map (all register-level)

Peripheral Pins Role
TIM3 PWM (CH1–CH4) PA6, PA7, PB0, PB1 4-channel motor PWM (~1 kHz)
USART2 + DMA1_S6 PA2 / PA3 Bluetooth HC-06 (9600 baud), non-blocking TX via DMA
USART3 PC10 / PC11 ESP32 link (115200 baud)
ADC1 + DMA2_S0 PC0 / PC1 / PC2 3 sensors, scan mode + circular DMA
TIM2 Triggers ADC conversions (TRGO)
TIM4 1 Hz tick → IoT scheduler (every 30 s)
EXTI0 PA0 User button — starts sampling
I2C2 PB10 / PB11 16×2 LCD (PCF8574)

Interrupt priorities are set explicitly so Bluetooth command reception (USART2) always preempts the slower background tasks.


Robot control logic

Bluetooth bytes are accumulated in an ISR and a full command is detected on the UART IDLE line (end-of-frame), then handled in the main loop:

if (strstr(cmd_buf, "AVANCE"))  { m1_avant = vitesse;     m2_avant = vitesse;     }
else if (strstr(cmd_buf, "GAUCHE")) { m1_avant = vitesse; m2_avant = vitesse / 2; }   // differential turn
else if (strstr(cmd_buf, "STOP"))   { vitesse = 200; }

TIM3->CCR1 = m1_avant;   TIM3->CCR2 = m1_arriere;
TIM3->CCR3 = m2_avant;   TIM3->CCR4 = m2_arriere;

Each movement command nudges the speed up (capped), turns are done by running one side at half speed, and STOP resets everything. The current command and speed are echoed back to the phone over Bluetooth.


Supervision layer (cloud + dashboard)

Beyond the firmware, I built the whole monitoring side, running three channels in parallel:

  • ThingSpeak — the three ADC values are pushed to a cloud channel over HTTP, giving a historical chart accessible from anywhere.
  • Mosquitto (MQTT) — the same values are published to a local broker on per-channel topics.
  • Node-RED — subscribes to the broker and renders the values as live gauges on a dashboard (/ui).

Since I no longer had the hardware on hand, I validated the supervision chain by simulating the sensor data via MQTT injection — the Node-RED flow and dashboard you see above run end-to-end against the broker, independent of the board.


Building it

The firmware is a single main.c using only CMSIS register definitions (<stm32f4xx.h>). To compile: create an STM32F407 project in Keil µVision or STM32CubeIDE and drop this main.c in place of the generated one. Set your own Wi-Fi / ThingSpeak / broker credentials in the #define block at the top before building.


What I'd improve

Add closed-loop speed control with wheel encoders instead of open-loop PWM, move the blocking delai_ms() calls off the hot path, and replace the polled ESP32 exchange with an interrupt- or DMA-driven one so the robot stays fully responsive during cloud uploads.


Tech stack

STM32F407 · Bare-metal (CMSIS, register access) · PWM · UART + DMA · ADC + DMA · I²C · EXTI · Bluetooth HC-06 · ESP32 · MQTT (Mosquitto) · ThingSpeak · Node-RED



🇫🇷 Version française

Robot IoT — STM32F407 bare-metal + supervision cloud

Un robot 4 roues piloté en Bluetooth, développé entièrement en bare-metal sur STM32F407 — chaque périphérique configuré en accès registre direct, sans HAL, sans code généré. PWM moteurs, parsing des commandes Bluetooth, acquisition multi-capteurs ADC en DMA, afficheur LCD I²C, et une couche complète de supervision cloud (ThingSpeak, MQTT, dashboard Node-RED).

Projet d'équipe (ENSIT). Ce dépôt couvre ma contribution : tout le firmware bare-metal et la couche de supervision (ThingSpeak, Mosquitto, Node-RED). Le volet commandes AT vers l'ESP32 a été développé par un binôme.


Architecture

Architecture du système Robot IoT


À voir en action

Dashboard Node-RED — jauges capteurs en temps réel

Dashboard Node-RED avec trois jauges ADC

ThingSpeak — historique cloud des trois canaux capteurs

Graphes ThingSpeak des trois champs ADC

Trace série (HTerm) — trafic AT / MQTT en fonctionnement

Capture HTerm des commandes AT et MQTT


Ce que ça fait

Un téléphone envoie des commandes texte (AVANCE, ARRIERE, GAUCHE, DROITE, STOP) en Bluetooth. Le STM32 les interprète et pilote quatre canaux PWM pour diriger un châssis 4 roues, en augmentant la vitesse à chaque commande de mouvement successive. En parallèle, trois capteurs analogiques sont échantillonnés en continu via ADC + DMA, affichés sur un LCD I²C local, renvoyés au téléphone, et publiés vers le cloud toutes les 30 secondes — en alternant entre ThingSpeak et un broker MQTT local. Un dashboard Node-RED s'abonne au broker et affiche les valeurs sous forme de jauges en temps réel.

Tout tourne depuis un seul main.c bare-metal : pas de HAL, pas de code généré par CubeMX, uniquement de l'accès registre CMSIS.


L'approche bare-metal

C'est la partie qui compte le plus pour moi. Chaque périphérique est configuré en écrivant directement dans ses registres — horloges, fonctions alternées, timers, flux DMA, priorités d'interruption. Par exemple, la PWM des moteurs est mise en place à la main :

void init_tim3_pwm(void)
{
    RCC->APB1ENR |= (1 << 1);
    TIM3->PSC     = 15;                          // 16 MHz / 16 = 1 MHz horloge timer
    TIM3->ARR     = 999;                         // → PWM 1 kHz
    TIM3->CCMR1  |= (6 << 4) | (6 << 12);        // mode PWM 1 sur CH1/CH2
    TIM3->CCMR2  |= (6 << 4) | (6 << 12);        // mode PWM 1 sur CH3/CH4
    TIM3->CCER   |= (1 << 0) | (1 << 4) | (1 << 8) | (1 << 12);
    TIM3->CR1    |= (1 << 0);                     // activation du compteur
}

Aucune couche d'abstraction ne masque ce que fait le matériel — ce qui rend le code compact, rapide, et démontre vraiment comment fonctionne le MCU sous la HAL.

Le MCU tourne sur son horloge HSI 16 MHz par défaut (pas de PLL), largement suffisante pour cette application et qui simplifie les calculs de timing.


Matériel

  • STM32F407VG-DISC1 — ARM Cortex-M4, sur l'oscillateur interne HSI 16 MHz
  • Module Bluetooth HC-06 — liaison sans fil de commande depuis un téléphone
  • Pont en H + châssis 4 roues à moteurs DC
  • 3 capteurs analogiques lus sur les canaux ADC (ex. lumière / IR / potentiomètre)
  • LCD I²C 16×2 (backpack PCF8574, adresse 0x27) pour l'état local
  • ESP32 (firmware AT) servant de passerelle Wi-Fi vers le cloud

Cartographie des périphériques (tout en registre)

Périphérique Broches Rôle
TIM3 PWM (CH1–CH4) PA6, PA7, PB0, PB1 PWM moteurs 4 canaux (~1 kHz)
USART2 + DMA1_S6 PA2 / PA3 Bluetooth HC-06 (9600 bauds), TX non bloquant en DMA
USART3 PC10 / PC11 Liaison ESP32 (115200 bauds)
ADC1 + DMA2_S0 PC0 / PC1 / PC2 3 capteurs, mode scan + DMA circulaire
TIM2 Déclenche les conversions ADC (TRGO)
TIM4 Tick 1 Hz → ordonnanceur IoT (toutes les 30 s)
EXTI0 PA0 Bouton utilisateur — lance l'acquisition
I2C2 PB10 / PB11 LCD 16×2 (PCF8574)

Les priorités d'interruption sont fixées explicitement pour que la réception des commandes Bluetooth (USART2) préempte toujours les tâches de fond plus lentes.


Logique de pilotage

Les octets Bluetooth sont accumulés dans une ISR et une commande complète est détectée sur la ligne IDLE de l'UART (fin de trame), puis traitée dans la boucle principale :

if (strstr(cmd_buf, "AVANCE"))  { m1_avant = vitesse;     m2_avant = vitesse;     }
else if (strstr(cmd_buf, "GAUCHE")) { m1_avant = vitesse; m2_avant = vitesse / 2; }   // virage différentiel
else if (strstr(cmd_buf, "STOP"))   { vitesse = 200; }

TIM3->CCR1 = m1_avant;   TIM3->CCR2 = m1_arriere;
TIM3->CCR3 = m2_avant;   TIM3->CCR4 = m2_arriere;

Chaque commande de mouvement augmente la vitesse (plafonnée), les virages se font en faisant tourner un côté à mi-vitesse, et STOP réinitialise tout. La commande courante et la vitesse sont renvoyées au téléphone en Bluetooth.


Couche de supervision (cloud + dashboard)

Au-delà du firmware, j'ai construit toute la partie monitoring, sur trois canaux en parallèle :

  • ThingSpeak — les trois valeurs ADC sont envoyées vers un canal cloud en HTTP, ce qui donne un historique graphique accessible de partout.
  • Mosquitto (MQTT) — les mêmes valeurs sont publiées sur un broker local, un topic par canal.
  • Node-RED — s'abonne au broker et affiche les valeurs sous forme de jauges en temps réel sur un dashboard (/ui).

N'ayant plus le matériel sous la main, j'ai validé la chaîne de supervision en simulant les données capteurs par injection MQTT — le flux et le dashboard Node-RED visibles plus haut fonctionnent de bout en bout face au broker, indépendamment de la carte.


Compilation

Le firmware tient dans un seul main.c n'utilisant que les définitions registre CMSIS (<stm32f4xx.h>). Pour compiler : créer un projet STM32F407 dans Keil µVision ou STM32CubeIDE et y placer ce main.c à la place de celui généré. Renseigner ses propres identifiants Wi-Fi / ThingSpeak / broker dans le bloc #define en haut avant compilation.


Pistes d'amélioration

Ajouter un asservissement de vitesse en boucle fermée avec des encodeurs de roues plutôt qu'une PWM en boucle ouverte, sortir les appels bloquants delai_ms() du chemin critique, et remplacer l'échange ESP32 par scrutation par une version sur interruption ou DMA pour que le robot reste pleinement réactif pendant les envois cloud.


Stack technique

STM32F407 · Bare-metal (CMSIS, accès registre) · PWM · UART + DMA · ADC + DMA · I²C · EXTI · Bluetooth HC-06 · ESP32 · MQTT (Mosquitto) · ThingSpeak · Node-RED

About

Bare-metal STM32F407 robot — register-level PWM, UART+DMA Bluetooth control, multi-channel ADC+DMA sensing, I²C LCD, and full cloud supervision stack (ThingSpeak, MQTT, Node-RED). No HAL, no generated code

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages