@ -1,54 +1,94 @@
# include <Arduino.h>
# include "IDECompat.h"
# include <WiFi.h>
# include <FS.h>
# include <SPIFFS.h>
# include <ESPAsyncWebServer.h>
# include "ADC.h"
# include "wifi-credentials.h"
AsyncWebServer server ( 80 ) ;
float averageBatteryVoltage = 0.0f ;
int16_t batteryVoltage = - 1 ; //in mV
ADC currentSensor ( 36 ) ;
ADC batterySensor ( 39 ) ;
const int8_t speedSensorPin = 13 ;
const int8_t debugLedPin = 12 ;
struct ADC_CalibrationValue
{
float Measure ;
int16_t ADC_Value ;
} ;
const ADC_CalibrationValue calibration [ ] = {
{ 0.118f , 1 } ,
{ 0.343f , 253 } ,
{ 0.791f , 801 } ,
{ 1.512f , 1705 } ,
{ 2.013f , 2306 } ,
{ 2.406f , 2796 } ,
{ 2.606f , 3058 } ,
{ 2.839f , 3423 } ,
{ 2.996f , 3726 } ,
{ 3.16f , 4094 }
} ;
const int8_t calibrationNumValues = sizeof ( calibration ) / sizeof ( calibration [ 0 ] ) ;
float getCalibratedVoltage ( int16_t adcOutput )
const float wheelDiameterInches = 20 ;
const int numImpulsesPerTurn = 2 ;
const float wheelCircumferenceMeters = wheelDiameterInches * 0.0254f * 3.1415f / ( float ) numImpulsesPerTurn ;
int16_t batteryVoltage = - 1 ; // in mV
int16_t batteryCurrent = - 1 ; // in mV
volatile bool debugLedState = true ;
volatile bool speedSensorState = false ;
volatile unsigned long speedSensorRiseTime = 0 ;
volatile unsigned long speedSensorLastImpulseTime = 0 ;
volatile unsigned long speedSensorLastImpulseInterval = ( unsigned long ) - 1 ; // in milliseconds
void IRAM_ATTR onSpeedSensorChange ( bool newState )
{
for ( int8_t i = 1 ; i < calibrationNumValues ; + + i )
if ( speedSensorState = = newState ) return ;
unsigned long now = millis ( ) ;
speedSensorState = newState ;
bool magnetDetected = ! speedSensorState ; // the magnet closes the contact which pulls the pin low
if ( magnetDetected )
{
speedSensorRiseTime = now ;
}
else
{
if ( i = = calibrationNumValues - 1 | | calibration [ i ] . ADC_Value > = adcOutput )
unsigned long impulseDuration = now > speedSensorRiseTime ? now - speedSensorRiseTime : ( 4294967295 - speedSensorRiseTime ) + now ; // if now is lower than speedSensorRiseTime, it means millis() has overflowed (happens every 50 days)
if ( impulseDuration > 1000 ) return ; // impulse was too long, ignore it (maybe magnet stopped near the sensor)
debugLedState = ! debugLedState ;
digitalWrite ( debugLedPin , debugLedState ? HIGH : LOW ) ;
unsigned long timeSinceLastImpulse = now > speedSensorLastImpulseTime ? now - speedSensorLastImpulseTime : ( 4294967295 - speedSensorLastImpulseTime ) + now ;
speedSensorLastImpulseTime = now ;
if ( timeSinceLastImpulse > 30 & & timeSinceLastImpulse < 4000 )
{
speedSensorLastImpulseInterval = timeSinceLastImpulse ;
}
else
{
const auto & p = calibration [ i - 1 ] ;
const auto & n = calibration [ i ] ;
return ( float ) ( adcOutput - p . ADC_Value ) / ( float ) ( n . ADC_Value - p . ADC_Value ) * ( n . Measure - p . Measure ) + p . Measure ;
speedSensorLastImpulseInterval = ( unsigned long ) - 1 ;
}
}
}
return - 1.0f ;
void IRAM_ATTR onSpeedSensorChange ( ) { onSpeedSensorChange ( digitalRead ( speedSensorPin ) = = HIGH ) ; }
float getSpeed ( )
{
unsigned long now = millis ( ) ;
unsigned long lastImpulseInterval = speedSensorLastImpulseInterval ;
unsigned long lastImpulseTime = speedSensorLastImpulseTime ;
unsigned long timeSinceLastImpulse = now > lastImpulseTime ? now - lastImpulseTime : ( 4294967295 - lastImpulseTime ) + now ;
unsigned long interval = timeSinceLastImpulse > lastImpulseInterval * 10 / 9 ? timeSinceLastImpulse : lastImpulseInterval ;
float speed = wheelCircumferenceMeters / ( float ) interval * 1000.0f ; // in meters per second
if ( speed < 0.25f ) return 0.0f ; // if speed is very low (less than 1km/h) it probably means we've stopped
return speed ;
}
void setup ( )
{
pinMode ( speedSensorPin , INPUT_PULLUP ) ;
attachInterrupt ( speedSensorPin , & onSpeedSensorChange , CHANGE ) ;
pinMode ( debugLedPin , OUTPUT ) ;
digitalWrite ( debugLedPin , debugLedState ? HIGH : LOW ) ;
Serial . begin ( 115200 ) ;
if ( ! SPIFFS . begin ( false ) ) {
@ -70,8 +110,11 @@ void setup()
server . on ( " /api/status " , HTTP_GET , [ ] ( AsyncWebServerRequest * request ) {
int v = batteryVoltage ;
int c = batteryCurrent ;
int s = ( int ) ( getSpeed ( ) * 1000.0f + 0.5f ) ;
char json [ 128 ] ;
sprintf ( json , " { \" v \" :%d, \" c \" :1000} " , v ) ;
sprintf ( json , " { \" v \" :%d, \" c \" :%d, \" s \" :%d } " , v , c , s ) ;
request - > send ( 200 , " text/json " , json ) ;
} ) ;
@ -86,22 +129,33 @@ void setup()
Serial . println ( " HTTP server started " ) ;
}
float avgAnalogV = 0.0f ;
void loop ( )
{
const int potPi n = 34 ;
const int numSamples = 100 ;
const float minV = 0.14f ; // 1
const float maxV = 3.16f ; // 4094
float averageV = 0.0f ;
float averageC = 0.0f ;
for ( int sample = 0 ; sample < numSamples ; + + sample )
{
delay ( 1 ) ;
float v = batterySensor . read ( ) ;
float c = currentSensor . read ( ) ;
delay ( 10 ) ;
int16_t analogV = analogRead ( potPin ) ;
float v = getCalibratedVoltage ( analogV ) ;
averageV + = v ;
averageC + = c ;
}
averageV / = ( float ) numSamples ;
averageC / = ( float ) numSamples ;
averageBatteryVoltage = averageBatteryVoltage * 0.95f + v * 0.05f ;
batteryVoltage = ( int16_t ) ( averageBatteryVoltage * 1000.0f + 0.5f ) ;
if ( averageV < 0.2f ) averageV = 0.0f ;
//avgAnalogV = avgAnalogV * 0.9f + (float)analogV * 0.1f;
//batteryVoltage = (int16_t)(avgAnalogV + 0.5f);
averageV * = 27.000f ; // account for voltage divider to retrieve the input voltage
averageC = max ( 0.0f , averageC - 2.5f ) / 0.0238f ; // convert voltage to current, according to the sensor linear relation
// TODO: mutex ?
batteryVoltage = ( int16_t ) ( averageV * 1000.0f + 0.5f ) ;
batteryCurrent = ( int16_t ) ( averageC * 1000.0f + 0.5f ) ;
delay ( 10 ) ;
}