BV-CONNECT: The Smart Connected Streetlight
Tired of inefficient street lighting and undetected hardware failures ? Meet our intelligent monitoring solution.
As part of my third year as a network and connected objects engineer at UniLaSalle Amiens, I created this prototype. Throughout the year, we discovered the theoretical aspect in class through various courses and practical work. All the knowledge we gained during the year was put into practice in this project.
Category: Showcase / Smart CityTimeline: ~50 hoursTech Stack: LoRaWAN, PHP, SQLite
1. BeginningThe inspiration for BV-CONNECT came to me one late winter evening while cycling back home through a particularly dim and isolated pathway near my neighborhood.
I realized then how static and inefficient our urban lighting remains; lamp posts are either burning energy in empty streets or left dark to save costs, leaving commuters feeling vulnerable. This experience became the foundation of my third-year project at the UniLaSalle Amiens engineering school, specifically within the Network Informatics and Connected Objects track. My goal was to move beyond simple automation and develop a proactive, smart street lighting system that perfectly balances energy optimization with citizen safety through real-time responsiveness.To bring this vision to life, I utilized LoRaWAN technology, a protocol ideally suited for smart city infrastructure due to its ability to maintain stable connections over long distances with minimal power requirements.The final result, BV-CONNECT, is an immersive platform where the interface background even changes in real-time to match the weather in Amiens and the lamp’s simulated internal clock. This project represents a comprehensive integration of mechanical engineering, low-power radio networking, and modern web development, offering a scalable solution for the future of intelligent urban management.
2. ContextDeveloped within the RIOC (Computer Networks and Connected Objects) program at UniLaSalle Amiens, BV-CONNECT modernizes urban infrastructure management. Physical assets like streetlights are often difficult to monitor manually; our solution bridges this gap using the Internet of Things (IoT).
The primary goal was to design a system capable of real-time environmental monitoring (temperature, luminosity, motion) while enabling remote control via the LoRaWAN protocol. This technology ensures long-range communication with ultra-low power consumption, making it ideal for distributed city-wide infrastructures.
3. Global logic and operationThe system operates by sending encrypted sensor data from the lamp node to a gateway, which then forwards it to The Things Network (TTN) cloud. Because our development environment was hosted on a Debian virtual machine within a private local network—preventing the use of public webhooks—I developed a custom PHP engine that regularly pulls data from the TTN Storage API.
This data is then processed and stored in a local SQLite database, powering a high-performance "Glassmorphism" dashboard that provides live updates on temperature, luminosity, and battery health without ever needing a page refresh.The project also involved a significant mechanical design phase to ensure the prototype was both functional and durable. I designed the entire structure and component housing using Onshape, focusing on a modern aesthetic that could fit seamlessly into a contemporary urban landscape.These designs were then 3D printed directly at the UniLaSalle lab, allowing for a rapid transition from digital model to physical hardware.
4. Features & FunctionalityAdministrator Dashboard
Managers access a modern, Glassmorphism-style web interface providing complete visibility over the device fleet :
- Real-time Monitoring: Instant visualization of temperature, ambient light levels, and occupancy rates (motion detection).
- Dynamic UI & Weather Sync: The dashboard background adapts in real-time to the device's simulated hour and actual Amiens weather conditions (via Open-Meteo API). If the device detects rain or storms, a custom Canvas engine renders animated raindrops on the interface.
- Remote Control (Downlinks): Operators can manually override the light state (ON / OFF) or revert it to "Auto" mode directly from the web portal.
Intelligent Alert System :
To ensure proactive maintenance, the system integrates automatic email notifications:
- Battery Alert: Instant notification if the power level drops below 10%.
- "Heartbeat" Timeout: If no data is received for more than 30 minutes, a "Connection Lost" alert is triggered to signal a potential hardware or network blackout.
- Light bulb maintenance : If the light bulb is not on during the hours it should be lit, then an email is sent.
It is a SODAQ Explorer board, chosen for its integrated Microchip RN2483 LoRaWAN module.
Power manager for the solar panel. It receives energy from the solar panel and redistributes it to the battery
Temperature sensor. Collects the current temperature from where the streetlamp is located.
Photoresistor light. Allows detection of the light level, which is primarily useful in the morning and evening. It also indicates whether the streetlight is switched on at the times it should be (useful for fault detection).
Buzzer alarm. It rings at specific times of the day to tell the time. Example: Rings 8 times at 8 a.m.
Clock module. It allows us to manage time and, in our case, to simulate.
Motion detection module. Allows the streetlight to be switched on only when necessary between 2am and 5am.
Lithium polymer battery. Allows the streetlamp to be self-powered.
First sketch :
3D plans :
The 3D design was created using Onshape.
Design :
The pole and lamppost head were 3D printed and the base was made using a laser cutter, cutting the components from 3mm plywood.
6. Software & Network ArchitectureBackend: PHP & SQLite
The system core uses a lightweight PHP backend with a SQLite database for rapid deployment and efficiency.
- sync.php: A synchronization script that fetches stored data from The Things Network (TTN) via the Storage Integration API.
- api.php: A REST API serving JSON data for the frontend charts (Chart.js) and status cards.
Network: LoRaWAN
Communication is bidirectional !
- Uplink: The device sends binary payloads to the LoRaWAN gateway.
From capture to visualization:
Downlink: The server pushes configuration to the device via TTN's MQTT broker.
From order to execution :
The code on the Arduino works as follows :
1. Initialization: Handshake with the TTN network using OTAA (Over-the-Air Activation).
2. Acquisition: Sampling data from the PIR, LDR, and temperature sensors.
3. Encoding: Payloads are byte-packed to minimize transmission time and bandwidth.
4. Transmission: Sending the Uplink and listening for an immediate Downlink to update the physical light state on the web dashboard.
Arduino code :
#include <Wire.h>
#include "RTClib.h"
#include <Sodaq_RN2483.h>
#define debugSerial SerialUSB
#define loraSerial Serial2
Sodaq_RN2483 loRa;
RTC_DS3231 rtc;
// --- Paramètres LoRaWAN ---
const uint8_t appEUI[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const uint8_t devEUI[8] = { 0x70, 0xB3, 0xD5, 0x7E, 0xD0, 0x07, 0x69, 0xF2 };
const uint8_t appKey[16] = { 0xFB, 0x57, 0x1B, 0x71, 0x5C, 0x43, 0xC1, 0x76, 0x2C, 0x05, 0x1D, 0xDC, 0xE7, 0x7C, 0x50, 0x64 };
// --- Définition des broches ---
const int pinBuzzer = 5;
const int pinLED = 6;
const int pinPIR = 7;
const int pinLuminosite = A1;
const int pinTemp = A2;
// Note : Pour la batterie, la constante BAT_VOLT est déjà définie en interne par Sodaq
// --- Chronomètres et Variables ---
unsigned long dernierEnvoiLoRa = 0;
const unsigned long intervalleEnvoi = 60000;
int modeManuel = 2; // 0 = OFF forcé, 1 = ON forcé, 2 = AUTO (planning)
int heureSimulee = 0;
int derniereHeureSonnee = -1;
bool premierEnvoi = true;
int compteurMouvement = 0;
bool mouvementEnCours = false;
void setup() {
debugSerial.begin(9600);
while ((!debugSerial) && (millis() < 10000));
debugSerial.println("--- Démarrage du Lampadaire (Mode Simulation 24h) ---");
pinMode(pinBuzzer, OUTPUT);
pinMode(pinLED, OUTPUT);
pinMode(pinPIR, INPUT_PULLDOWN);
// Configuration de la résolution analogique à 12 bits pour le SAMD21 (Sodaq)
analogReadResolution(12);
if (!rtc.begin()) {
debugSerial.println("Horloge RTC introuvable.");
}
loraSerial.begin(57600);
loRa.setDiag(debugSerial);
if (loRa.initOTA(loraSerial, devEUI, appEUI, appKey)) {
debugSerial.println("Réseau TTN connecté !");
}
}
void sonnerHeure(int nombreDeBips) {
for (int i = 0; i < nombreDeBips; i++) {
digitalWrite(pinBuzzer, HIGH);
delay(150);
digitalWrite(pinBuzzer, LOW);
delay(150);
}
}
void loop() {
bool declencherEnvoi = false;
// --- 1. GESTION DU TEMPS ---
if (premierEnvoi) {
declencherEnvoi = true;
premierEnvoi = false;
dernierEnvoiLoRa = millis();
}
else if (millis() - dernierEnvoiLoRa >= intervalleEnvoi) {
heureSimulee++;
if (heureSimulee > 23) {
heureSimulee = 0;
}
dernierEnvoiLoRa = millis();
declencherEnvoi = true;
}
// --- 2. LOGIQUE DE COMPTAGE DU MOUVEMENT ---
int pirState = digitalRead(pinPIR);
if (pirState == HIGH && !mouvementEnCours) {
compteurMouvement++;
mouvementEnCours = true;
debugSerial.print("Mouvement détecté ! Total : ");
debugSerial.println(compteurMouvement);
} else if (pirState == LOW) {
mouvementEnCours = false;
}
// --- 3. LOGIQUE MÉTIER ---
bool lampeDoitEtreAllumee = false;
if (heureSimulee >= 0 && heureSimulee <= 1) lampeDoitEtreAllumee = true;
else if (heureSimulee >= 2 && heureSimulee <= 5) {
if (pirState == HIGH) lampeDoitEtreAllumee = true;
}
else if (heureSimulee >= 6 && heureSimulee <= 7) lampeDoitEtreAllumee = true;
else if (heureSimulee >= 8 && heureSimulee <= 18) lampeDoitEtreAllumee = false;
else if (heureSimulee >= 19 && heureSimulee <= 23) lampeDoitEtreAllumee = true;
if (modeManuel == 1) {
digitalWrite(pinLED, HIGH);
}
else if (modeManuel == 0) {
digitalWrite(pinLED, LOW);
}
else {
digitalWrite(pinLED, lampeDoitEtreAllumee ? HIGH : LOW);
}
// --- 4. GESTION DU BUZZER ---
if (heureSimulee != derniereHeureSonnee) {
if (heureSimulee == 8) sonnerHeure(8);
if (heureSimulee == 12) sonnerHeure(12);
if (heureSimulee == 20) sonnerHeure(8);
derniereHeureSonnee = heureSimulee;
}
// --- 5. MESURES & TRANSMISSION ---
if (declencherEnvoi) {
bool etatPrecedentLed = digitalRead(pinLED);
digitalWrite(pinLED, LOW);
delay(50);
// Lectures analogiques (adaptées au mode 12 bits)
int lumiereAmbiante = analogRead(pinLuminosite);
analogRead(pinTemp);
analogRead(pinTemp);
delay(20);
int valeurTempBrute = analogRead(pinTemp);
float voltageTemp = (valeurTempBrute / 4095.0) * 3.3;
float temperature = voltageTemp * 100.0;
// --- NOUVELLE MESURE DE LA BATTERIE (SODAQ) ---
int rawBatt = analogRead(BAT_VOLT);
// Le diviseur interne coupe la tension par 2, on multiplie par 2 pour retrouver la vraie valeur
float tensionBatterie = (rawBatt / 4095.0) * 3.3 * 2.0;
// Estimation du pourcentage (LiPo : 3.5V vide, 4.2V pleine pour préserver la batterie)
float pctBattFloat = (tensionBatterie - 3.5) / (4.2 - 3.5) * 100.0;
int pourcentageBatterie = constrain((int)pctBattFloat, 0, 100);
digitalWrite(pinLED, etatPrecedentLed);
debugSerial.println("\n--- NOUVEL ENVOI ---");
debugSerial.print("Valeurs -> Temp: "); debugSerial.print(temperature, 1);
debugSerial.print(" | Lum: "); debugSerial.print(lumiereAmbiante);
debugSerial.print(" | Cumul Mvmt: "); debugSerial.print(compteurMouvement);
debugSerial.print(" | Heure: "); debugSerial.print(heureSimulee);
debugSerial.print(" | Batterie: "); debugSerial.print(pourcentageBatterie); debugSerial.println("%");
debugSerial.print("Mode Actuel: "); debugSerial.println(modeManuel);
// Formatage binaire (Passage à 7 octets)
int tempToSend = (int)(temperature * 10.0);
uint8_t payload[7];
payload[0] = highByte(tempToSend);
payload[1] = lowByte(tempToSend);
payload[2] = highByte(lumiereAmbiante);
payload[3] = lowByte(lumiereAmbiante);
payload[4] = compteurMouvement;
payload[5] = heureSimulee;
payload[6] = (uint8_t)pourcentageBatterie; // AJOUT : 7ème octet pour la batterie
// Envoi des 7 octets
if (loRa.sendReqAck(1, payload, 7, 2)) {
debugSerial.println("-> Transmis (ACK reçu !)");
} else {
debugSerial.println("-> Transmis (Pas d'ACK reçu, vérification du réseau...)");
}
compteurMouvement = 0;
delay(500);
uint8_t rxBuffer[10];
uint8_t rxLength = loRa.receive(rxBuffer, sizeof(rxBuffer));
if (rxLength > 0) {
modeManuel = rxBuffer[0];
debugSerial.print(">>> DOWNLINK RECU ! Nouveau mode manuel : ");
debugSerial.println(modeManuel);
}
}
delay(100);
}BV-CONNECT was successfully delivered within a 50-hour window, proving the feasibility of LoRaWAN for Smart City applications.
Future Roadmap:
- Scalability: Deploying a multi-node network to test signal mesh density.
- Predictive Maintenance: Using Machine Learning to predict lighting needs based on historical motion patterns.
- Environmental Expansion: Integrating Air Quality sensors (CO2/PM2.5) into the existing node.
I would like to express my sincere gratitude to Mr. Dailly, research professor at UniLaSalle Amiens and my LoRa instructor, for his invaluable guidance. Special thanks go to the makerspace team for their practical assistance in building the physical structure of the street lamp.
Finally, I am deeply grateful to the entire UniLaSalle staff who made this project possible.













Comments