Created
December 7, 2023 10:57
-
-
Save g1ra/d8fb2eae4e125098fe222ef9df3f790e to your computer and use it in GitHub Desktop.
ESP32_S2_Entrance_GPS_Radar_Light_v2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// a few defines to abstract away the Serial-specific stuff | |
#define DEBUG 1 | |
#if DEBUG == 1 | |
#define PR(...) SERIAL_DEVICE.print(__VA_ARGS__) | |
#define PRF(...) SERIAL_DEVICE.printf(__VA_ARGS__) | |
#define PRLN(...) SERIAL_DEVICE.println(__VA_ARGS__) | |
#define LN() PRLN(' ') | |
#define LINES(n) for (uint8_t _bl=0; _bl<n; _bl++) { LN(); } | |
#else | |
#define PR(...) | |
#define PRF(...) | |
#define PRLN(...) | |
#define LN() | |
#define LINES(n) | |
#endif | |
#define SERIAL_BAUDRATE 115200 | |
#define SERIAL_DEVICE Serial | |
#define TESTINGBUTTONPIN 16 | |
#define POWERLED 15 | |
#define MAXLEDTEMP 60 | |
unsigned long detectTimeout = 2500; | |
unsigned long LCDOffTimeout = 10000; // LCD will turn off after X ms, if not movement detected | |
unsigned long detectLastSeen = 0; | |
// https://www.wemos.cc/en/latest/_static/boards/s2_pico_v1.0.0_4_16x9.png | |
// https://www.wemos.cc/en/latest/_static/files/sch_s2_pico_v1.0.0.pdf | |
// https://www.weigu.lu/microcontroller/tips_tricks/esp32_tips_tricks/index.html | |
// https://www.studiopieters.nl/esp32-s2-pinout/ | |
// SDA IO8 | |
// SCL IO9 | |
///////////////////////////////////////////////////////////////// | |
#include <OneWire.h> | |
#include <DallasTemperature.h> | |
#define TEMPERATURE_PRECISION 9 | |
#define ONE_WIRE_BUS_DS18B20 17 | |
OneWire oneWireDS18B20(ONE_WIRE_BUS_DS18B20); | |
DallasTemperature sensorDS18B20(&oneWireDS18B20); | |
float tempDS18B20 = 0; | |
DeviceAddress tempDeviceAddress; | |
float getDS18B20Temp() { | |
sensorDS18B20.requestTemperatures(); // Send the command to get temperatures | |
// We use the function ByIndex, and as an example get the temperature from the first sensor only. | |
float tempC = sensorDS18B20.getTempCByIndex(0); | |
// Check if reading was successful | |
// if(tempC != DEVICE_DISCONNECTED_C) { | |
// PR("Temperature for the device 1 (index 0) is: "); | |
// PRLN(tempC); | |
// } else { | |
// PRLN("Error: Could not read temperature data"); | |
// } | |
return tempC; | |
} | |
void printDS18B20Address(DeviceAddress deviceAddress) | |
{ | |
for (uint8_t i = 0; i < 8; i++) | |
{ | |
if (deviceAddress[i] < 16) PR("0"); | |
PR(deviceAddress[i], HEX); | |
} | |
} | |
///////////////////////////////////////////////////////////////// | |
// DS3231 | |
// DS3231S RTC chip’s fixed I2C address is 0x68 | |
// EEPROM’s default I2C address is 0x57 (though the address range is 0x50 to 0x57). | |
#include <Wire.h> | |
#include <RtcDS3231.h> | |
TwoWire I2Cone = TwoWire(0); // for LCD and RTC | |
RtcDS3231<TwoWire> Rtc(I2Cone); | |
#define countof(a) (sizeof(a) / sizeof(a[0])) | |
void printDateTime(const RtcDateTime& dt) | |
{ | |
char datestring[26]; | |
snprintf_P(datestring, | |
countof(datestring), | |
PSTR("%02u/%02u/%04u %02u:%02u:%02u"), | |
dt.Month(), | |
dt.Day(), | |
dt.Year(), | |
dt.Hour(), | |
dt.Minute(), | |
dt.Second() ); | |
PR(datestring); | |
} | |
///////////////////////////////////////////////////////////////// | |
#include <Adafruit_ADS1X15.h> | |
//Adafruit_ADS1115 ads; /* Use this for the 16-bit version */ | |
Adafruit_ADS1015 ads; /* Use this for the 12-bit version */ | |
TwoWire I2Ctwo = TwoWire(1); // address 0x3C | |
bool initADC = false; | |
#define TEMPERATURENOMINAL 25 | |
#define DEFAULT_BCOEF 3950 | |
#define DEFAULT_NOMINAL_RES 10000 | |
#define SERIESRESISTOR 10000 | |
// GND -> THERMISTOR one leg | |
// THERMISTOR other leg <-> A0 | |
// A0 <-> 100K resistor | |
// 100K resistor to 3.3Volt | |
float calcNTC() { | |
int16_t adc; | |
float volts; | |
adc = ads.readADC_SingleEnded(0); | |
volts = ads.computeVolts(adc); | |
// PRLN("-----------------------------------------------------------"); | |
// PR("AIN0: "); PR(adc); PR(" "); PR(volts); PRLN("V"); | |
float val = 3.3 / volts - 1; | |
float resistance = SERIESRESISTOR / val; | |
// PR("Thermistor resistance "); | |
// PRLN(resistance); | |
float steinhart; | |
steinhart = resistance / DEFAULT_NOMINAL_RES; | |
steinhart = log(steinhart); | |
steinhart /= DEFAULT_BCOEF; | |
steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); | |
steinhart = 1.0 / steinhart; | |
steinhart -= 273.15; | |
return steinhart; | |
} | |
///////////////////////////////////////////////////////////////// | |
//------------------------------------------ | |
#include <Bounce2.h> //https://github.com/thomasfredericks/Bounce2 | |
Bounce2::Button testing_button = Bounce2::Button(); | |
bool testing_state = false; | |
//------------------------------------------ | |
//------------------------------------------ | |
// https://github.com/contrem/arduino-timer | |
#include <arduino-timer.h> | |
auto timer = timer_create_default(); | |
//------------------------------------------ | |
//------------------------------------------ | |
// must fix in /home/bob/Arduino/libraries/Chronos/src/chronosinc/timeExtInc.h line 43 : #include <TimeLib.h> | |
#include <Chronos.h> // https://inductive-kickback.com/projects/chronos/ | |
Chronos::DateTime sunSetc; | |
Chronos::DateTime sunRisec; | |
bool invalidTime = false; | |
//------------------------------------------ | |
//----------------------------- | |
#include <SolarCalculator.h> | |
double latitude = 49.3811; | |
double longitude = 16.4686; | |
int utc_offset = 1; | |
double transit, sunrise_d, sunset_d; // Event times, in hours (UTC) | |
bool sunset = false; | |
void calcSunset() { | |
Chronos::DateTime nowTime(Chronos::DateTime::now()); | |
time_t utc = now(); | |
calcSunriseSunset(utc, latitude, longitude, transit, sunrise_d, sunset_d); | |
Chronos::DateTime midnight(year(), month(), day(), 0, 0, 0); | |
sunSetc = midnight + Chronos::Span::Seconds(int(60*60*(sunset_d + utc_offset))); | |
sunRisec = midnight + Chronos::Span::Seconds(int(60*60*(sunrise_d + utc_offset))); | |
sunSetc.printTo(Serial); PR(" "); sunRisec.printTo(Serial); PR(" "); nowTime.printTo(Serial); LN(); | |
if ( (sunSetc < nowTime) || (sunRisec > nowTime) ) { | |
sunset = true; | |
} else { | |
sunset = false; | |
} | |
} | |
//----------------------------- | |
// http://arduiniana.org/libraries/tinygps/comment-page-12/ | |
#include <TinyGPSMinus.h> | |
#include <TimeLib.h> | |
// https://www.engineersgarage.com/gps-module-with-arduino/ | |
#define GPSBAUD 9600 | |
TinyGPSMinus gps; | |
//get datetime : libraries/Time/examples/TimeGPS/TimeGPS.ino | |
time_t prevDisplay = 0; // when the digital clock was displayed | |
const int tzoffset = 1; // Central European Time | |
// ESP32 S2 have only 2 UART . One is Serial , other can be user | |
#include <HardwareSerial.h> // https://microcontrollerslab.com/esp32-uart-communication-pins-example/ | |
// https://microcontrollerslab.com/esp32-uart-communication-pins-example/ | |
HardwareSerial HSerialGPS(1); // RX: 4, TX: 5 | |
//----------------------------- | |
// LD1125H https://github.com/UsefulElectronics/esp32s3-gc9a01-lvgl | |
#define LEDPIN 10 | |
#define RADARPIN 6 | |
bool radarDetect = false; | |
// AM312 | |
#define PIRPIN 7 | |
bool pirDetect = false; | |
//----------------------------- | |
#include <SPI.h> | |
#include <Adafruit_GFX.h> | |
#include <Adafruit_SSD1306.h> | |
#include <Fonts/FreeMonoBold9pt7b.h> // https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts | |
// #include <Font5x7FixedMono.h> // https://github.com/robjen/GFX_fonts | |
#define SCREEN_WIDTH 128 // OLED display width, in pixels | |
#define SCREEN_HEIGHT 32 // OLED display height, in pixels | |
#define OLED_RESET 18 // Reset pin # (or -1 if sharing Arduino reset pin) | |
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 | |
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &I2Cone, OLED_RESET); | |
// 'sun', 32x32px | |
const unsigned char sun_bitmap [] PROGMEM = { | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, | |
0x00, 0x01, 0x80, 0x00, 0x02, 0x01, 0x80, 0x40, 0x07, 0x00, 0x00, 0xe0, 0x03, 0x80, 0x01, 0xc0, | |
0x01, 0x87, 0xe3, 0x80, 0x00, 0x9f, 0xf1, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x00, 0x7f, 0xfc, 0x00, | |
0x00, 0x7f, 0xfe, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x00, 0x7c, 0xff, 0xff, 0x3c, | |
0x7c, 0xff, 0xff, 0x3e, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xfe, 0x00, | |
0x00, 0x7f, 0xfe, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x01, 0x87, 0xe3, 0x80, | |
0x03, 0x80, 0x01, 0xc0, 0x07, 0x00, 0x00, 0xe0, 0x06, 0x01, 0x80, 0x40, 0x00, 0x01, 0x80, 0x00, | |
0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 | |
}; | |
// 'moon', 32x32px | |
const unsigned char moon_bitmap [] PROGMEM = { | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, | |
0x00, 0xfc, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00, | |
0x07, 0xf0, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, | |
0x1f, 0xf8, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x00, 0x1f, 0x9c, 0x00, 0x00, 0x1f, 0x8e, 0x00, 0x00, | |
0x1f, 0xdf, 0x80, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x1f, 0xff, 0x00, 0x00, | |
0x0f, 0xf7, 0x80, 0x00, 0x0f, 0xf8, 0x60, 0x00, 0x07, 0xff, 0xfc, 0x30, 0x07, 0xff, 0xff, 0xf0, | |
0x03, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xc0, 0x00, 0xff, 0xff, 0x80, 0x00, 0x3f, 0xfe, 0x00, | |
0x00, 0x0f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | |
}; | |
bool updateDS18B20Temp(void *) { | |
tempDS18B20 = getDS18B20Temp(); | |
return true; | |
} | |
bool updateSystem(void *) { | |
// float tempDS18B20 = getDS18B20Temp(); | |
// PR("tempDS18B20 : "); PRLN(tempDS18B20); | |
if ( !invalidTime ) { | |
calcSunset(); | |
} | |
if (digitalRead(RADARPIN) == 1) { | |
radarDetect = true; | |
PR("RADAR "); | |
digitalWrite(LEDPIN, LOW); //turn LED ON | |
detectLastSeen = millis(); | |
} else { | |
radarDetect = false; | |
digitalWrite(LEDPIN, HIGH); //turn LED OFF | |
} | |
if (digitalRead(PIRPIN) == 1) { | |
PR("PIR "); | |
pirDetect = true; | |
detectLastSeen = millis(); | |
} else { | |
pirDetect = false; | |
} | |
float NTCtemp = 0; | |
PR("DS18B20 Temperature :"); PR(tempDS18B20); PR(" *C"); | |
if (initADC) { | |
NTCtemp = calcNTC(); | |
PR(" | NTC Temperature "); | |
PR(NTCtemp); | |
PR(" *C"); | |
} else { | |
if (!ads.begin(0x48, &I2Ctwo)) { | |
PRLN("Failed to initialize ADS."); | |
initADC = false; | |
} else { | |
initADC = true; | |
} | |
} | |
PR("detectLastSeen: "); | |
PRLN( ( millis() - detectLastSeen) / 1000 ); | |
if (testing_state) { | |
//testing POWERLED | |
digitalWrite(POWERLED, HIGH); | |
PRLN("testing_state HIGH"); | |
} else { | |
PRLN("testing_state LOW"); | |
if (millis() > detectLastSeen + detectTimeout) { | |
digitalWrite(POWERLED, LOW); | |
PRLN("NO MOVEMENT within detectTimeout. turn off POWERLED "); | |
} else { | |
digitalWrite(POWERLED, HIGH); | |
} | |
if ( invalidTime ) { | |
digitalWrite(POWERLED, LOW); | |
PR("invalid date/time. BAD Year . "); PRLN(year()); | |
} | |
} | |
// if POWERLED temperature is greater than max allowed then turn POWERLED OFF | |
if (NTCtemp > MAXLEDTEMP) { | |
digitalWrite(POWERLED, LOW); | |
PRLN("MAX POWERLED temperature reached !"); | |
} | |
LCDupdate(); | |
return true; | |
} | |
void setup() { | |
SERIAL_DEVICE.begin(SERIAL_BAUDRATE); | |
PR("compiled: "); | |
PR(__DATE__); | |
PRLN(__TIME__); | |
pinMode(POWERLED, OUTPUT); | |
HSerialGPS.begin(GPSBAUD, SERIAL_8N1, 4, 5); | |
I2Ctwo.begin(33, 35); | |
ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2mV 0.125mV | |
if (!ads.begin(0x48, &I2Ctwo)) { | |
delay(2000); | |
PRLN("Failed to initialize ADS."); | |
initADC = false; | |
} else { | |
initADC = true; | |
} | |
pinMode(RADARPIN, INPUT_PULLUP); | |
pinMode(PIRPIN, INPUT_PULLUP); | |
pinMode(LEDPIN, OUTPUT); | |
I2Cone.begin(8, 9); // SCREEN_ADDRESS 0x3C LCD | |
///////////////// | |
Rtc.Begin(); | |
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); | |
printDateTime(compiled); | |
// never assume the Rtc was last configured by you, so | |
// just clear them to your needed state | |
Rtc.Enable32kHzPin(false); | |
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); | |
///////////////// | |
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally | |
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { | |
PRLN(F("SSD1306 allocation failed")); | |
for(;;); // Don't proceed, loop forever | |
} | |
sensorDS18B20.begin(); | |
delay(2000); // Pause for 1 seconds | |
int numberOfDevices = sensorDS18B20.getDeviceCount(); | |
PR("Found "); | |
PR(numberOfDevices, DEC); | |
PRLN(" devices."); | |
for(int i=0;i<numberOfDevices; i++) { | |
if(sensorDS18B20.getAddress(tempDeviceAddress, i)) { | |
PR("Found device "); | |
PR(i, DEC); | |
PR(" with address: "); | |
printDS18B20Address(tempDeviceAddress); | |
LN(); | |
PR("Setting resolution to "); | |
PRLN(TEMPERATURE_PRECISION, DEC); | |
// set the resolution to TEMPERATURE_PRECISION bit (Each Dallas/Maxim device is capable of several different resolutions) | |
sensorDS18B20.setResolution(tempDeviceAddress, TEMPERATURE_PRECISION); | |
PR("Resolution actually set to: "); | |
PR(sensorDS18B20.getResolution(tempDeviceAddress), DEC); | |
PRLN(); | |
} else { | |
PR("Found ghost device at "); | |
PR(i, DEC); | |
PR(" but could not detect address. Check power and cabling"); | |
} | |
} | |
// BUTTON SETUP | |
testing_button.attach( TESTINGBUTTONPIN, INPUT_PULLUP ); // USE INTERNAL PULL-UP | |
testing_button.interval(5); // DEBOUNCE INTERVAL IN MILLISECONDS | |
// INDICATE THAT THE LOW STATE CORRESPONDS TO PHYSICALLY PRESSING THE BUTTON | |
testing_button.setPressedState(LOW); | |
calcSunset(); | |
display.display(); | |
delay(1000); // Pause for 1 seconds | |
// Clear the buffer | |
display.clearDisplay(); | |
timer.every(500, updateSystem); | |
timer.every(2000, updateDS18B20Temp); | |
} | |
void loop() { | |
timer.tick(); // need for arduino-timer , must Call timer.tick() in the loop function | |
testing_button.update(); | |
if (testing_button.pressed()) { | |
testing_state = !testing_state; // flip | |
} | |
if ( (timeStatus() != timeNotSet) && (year() != 2080) && (year() != 1970) ) { | |
invalidTime = false; | |
} else { | |
invalidTime = true; | |
} | |
// For one second we parse GPS data and report some key values | |
char lat[9], lon[10]; | |
unsigned long age, date, time, chars = 0; | |
unsigned short sentences = 0, failed = 0; | |
while (HSerialGPS.available()) { | |
unsigned short sentences, failed_checksum; | |
unsigned long chars; | |
if (gps.encode(HSerialGPS.read())) { // process gps messages | |
// when TinyGPS reports new data... | |
// The stats method provides a clue whether you are getting good data or not. | |
// It provides statistics that help with troubleshooting. | |
gps.stats(&chars, &sentences, &failed_checksum); | |
// chars – the number of characters fed to the object | |
// sentences – the number of valid $GPGGA and $GPRMC sentences processed | |
// failed_checksum – the number of sentences that failed the checksum test | |
//PR("chars :"); PR(chars); PR(" sentences:"); PR(sentences); PR(" failed_checksum:"); PRLN(failed_checksum); | |
unsigned long age; | |
int Year; | |
byte Month, Day, Hour, Minute, Second; | |
gps.crack_datetime(&Year, &Month, &Day, &Hour, &Minute, &Second, NULL, &age); | |
if (age < 500) { | |
// set the Time to the latest GPS reading | |
setTime(Hour, Minute, Second, Day, Month, Year); | |
adjustTime(tzoffset * SECS_PER_HOUR); | |
} | |
} | |
} | |
if (timeStatus()!= timeNotSet) { | |
if (now() != prevDisplay) { //update the display only if the time has changed | |
prevDisplay = now(); | |
} | |
} | |
} | |
void LCDupdate(){ | |
display.clearDisplay(); | |
if (millis() > detectLastSeen + LCDOffTimeout) { | |
display.display(); | |
return; | |
} | |
display.setTextSize(1); // Normal 1:1 pixel scale | |
display.setFont(NULL); //&FreeMonoBold9pt7b); | |
display.setTextColor(SSD1306_WHITE); // Draw white text | |
display.setCursor(0, 5); // Start at top-left corner | |
if (testing_state) { | |
display.print("T"); | |
} | |
if ( !invalidTime ) { | |
display.print(year()); | |
display.print("/"); | |
display.print(month()); | |
display.print("/"); | |
display.print(day()); | |
display.print(" "); | |
} else { | |
display.print("invalid "); | |
} | |
float NTCtemp; | |
NTCtemp = calcNTC(); | |
display.println(NTCtemp); | |
if ( !invalidTime ) { | |
char uhours[8]; char uminutes[8]; char useconds[8]; | |
sprintf(uhours, "%02u", hour()); | |
sprintf(uminutes, "%02u", minute()); | |
sprintf(useconds, "%02u", second()); | |
display.print(uhours); display.print(":"); | |
display.print(uminutes); display.print(":"); | |
display.print(useconds); | |
} else { | |
display.print("--:--:--"); | |
} | |
display.print(" "); | |
display.println(tempDS18B20); | |
if(pirDetect) { | |
display.print("PIR "); | |
} | |
if(radarDetect) { | |
display.print("RADAR "); | |
} | |
if (!invalidTime) { | |
if (sunset) { | |
display.drawBitmap(94, 0, moon_bitmap, 32, 32, WHITE); | |
} else { | |
display.drawBitmap(94, 0, sun_bitmap, 32, 32, WHITE); | |
} | |
} | |
if (millis() < detectLastSeen + detectTimeout) { | |
display.invertDisplay(true); | |
} else { | |
display.invertDisplay(false); | |
} | |
display.display(); | |
} | |
void printDigits(int digits) { | |
// utility function for digital clock display: prints preceding colon and leading 0 | |
display.print(":"); | |
if(digits < 10) | |
display.print('0'); | |
display.print(digits); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment