update game
This commit is contained in:
@@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"tags": [{
|
"tags": [{
|
||||||
"TagUID" : "B3 26 D0 15",
|
"TagUID" : "B3 26 D0 15",
|
||||||
"audiofile" : "Billy-Jean.mp3"
|
"audiofile" : "/Billy-Jean.mp3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"TagUID" : "1F E8 20 00",
|
"TagUID" : "1F E8 20 00",
|
||||||
"audiofile" : "Let_it_be.mp3"
|
"audiofile" : "/Let_it_be.mp3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"TagUID" : "63 7E 1B 18",
|
"TagUID" : "23 0E 2C 19",
|
||||||
"audiofile" : "ringoffire.mp3"
|
"audiofile" : "/ringoffire.mp3"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ AudioFileSourceLittleFS *file;
|
|||||||
AudioOutputI2S *out;
|
AudioOutputI2S *out;
|
||||||
|
|
||||||
uint8_t audio_current_Song = 0;
|
uint8_t audio_current_Song = 0;
|
||||||
const char* nextAudioFile = "";
|
const char *nextAudioFile = "";
|
||||||
uint8_t n = 0;
|
uint8_t n = 0;
|
||||||
|
|
||||||
bool audioState = false;
|
bool audioState = false;
|
||||||
@@ -21,8 +21,28 @@ const char *waveFile[] =
|
|||||||
// Called when a metadata event occurs (i.e. an ID3 tag, an ICY block, etc.
|
// Called when a metadata event occurs (i.e. an ID3 tag, an ICY block, etc.
|
||||||
void MDCallback(void *cbData, const char *type, bool isUnicode, const char *string)
|
void MDCallback(void *cbData, const char *type, bool isUnicode, const char *string)
|
||||||
{
|
{
|
||||||
|
// (void)cbData;
|
||||||
|
// log_i("ID3 callback for: %s = '", type);
|
||||||
|
|
||||||
(void)cbData;
|
(void)cbData;
|
||||||
log_i("ID3 callback for: %s = '", type);
|
Serial.printf("ID3 callback for: %s = '", type);
|
||||||
|
|
||||||
|
if (isUnicode)
|
||||||
|
{
|
||||||
|
string += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*string)
|
||||||
|
{
|
||||||
|
char a = *(string++);
|
||||||
|
if (isUnicode)
|
||||||
|
{
|
||||||
|
string++;
|
||||||
|
}
|
||||||
|
Serial.printf("%c", a);
|
||||||
|
}
|
||||||
|
Serial.printf("'\n");
|
||||||
|
Serial.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when there's a warning or error (like a buffer underflow or decode hiccup)
|
// Called when there's a warning or error (like a buffer underflow or decode hiccup)
|
||||||
@@ -36,52 +56,54 @@ void StatusCallback(void *cbData, int code, const char *string)
|
|||||||
log_i("STATUS(%s) '%d' = '%s'\n", ptr, code, s1);
|
log_i("STATUS(%s) '%d' = '%s'\n", ptr, code, s1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void playSong(uint8_t index)
|
// void playSong(uint8_t index)
|
||||||
{
|
// {
|
||||||
if (index > AUDIONSONGS)
|
// if (index > AUDIONSONGS)
|
||||||
return;
|
// return;
|
||||||
log_i("now playing %s\n", waveFile[index]);
|
// log_i("now playing %s\n", waveFile[index]);
|
||||||
file = new AudioFileSourceLittleFS(waveFile[index]);
|
// file = new AudioFileSourceLittleFS(waveFile[index]);
|
||||||
id3 = new AudioFileSourceID3(file);
|
// id3 = new AudioFileSourceID3(file);
|
||||||
id3->RegisterMetadataCB(MDCallback, (void *)"ID3TAG");
|
// id3->RegisterMetadataCB(MDCallback, (void *)"ID3TAG");
|
||||||
mp3->begin(id3, out);
|
// mp3->RegisterStatusCB(StatusCallback, (void *)"mp3");
|
||||||
}
|
// mp3->begin(id3, out);
|
||||||
|
// }
|
||||||
|
|
||||||
void playSong(String filename)
|
void playSong(String filename)
|
||||||
{
|
{
|
||||||
if(filename != "")
|
if (filename != "")
|
||||||
{
|
{
|
||||||
log_i("now playing %s\n", filename.c_str());
|
log_i("now playing %s\n", filename.c_str());
|
||||||
file = new AudioFileSourceLittleFS(filename.c_str());
|
file = new AudioFileSourceLittleFS(filename.c_str());
|
||||||
id3 = new AudioFileSourceID3(file);
|
id3 = new AudioFileSourceID3(file);
|
||||||
id3->RegisterMetadataCB(MDCallback, (void *)"ID3TAG");
|
id3->RegisterMetadataCB(MDCallback, (void *)"ID3TAG");
|
||||||
|
mp3->RegisterStatusCB(StatusCallback, (void *)"mp3");
|
||||||
mp3->begin(id3, out);
|
mp3->begin(id3, out);
|
||||||
}
|
}
|
||||||
else{
|
else
|
||||||
|
{
|
||||||
log_e("no filenae specified");
|
log_e("no filenae specified");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void initAudio()
|
void initAudio()
|
||||||
{
|
{
|
||||||
log_i("init Audio");
|
log_i("init Audio");
|
||||||
audioLogger = &Serial;
|
audioLogger = &Serial;
|
||||||
delay(500);
|
delay(500);
|
||||||
out = new AudioOutputI2S();
|
out = new AudioOutputI2S();
|
||||||
out->SetPinout(I2S_BCLK, I2S_WCLK, I2S_DATA); //bclk, wclk, data
|
out->SetPinout(I2S_BCLK, I2S_WCLK, I2S_DATA); // bclk, wclk, data
|
||||||
out->SetGain(AUDIOGAIN);
|
out->SetGain(AUDIOGAIN);
|
||||||
|
|
||||||
mp3 = new AudioGeneratorMP3();
|
mp3 = new AudioGeneratorMP3();
|
||||||
mp3->RegisterStatusCB(StatusCallback, (void *)"mp3");
|
|
||||||
audioInitOk = true;
|
audioInitOk = true;
|
||||||
log_i("init Audio Done");
|
log_i("init Audio Done");
|
||||||
//playSong(audio_current_Song);
|
playSong("/Billy-Jean.mp3");
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAudioState(bool state)
|
void setAudioState(bool state)
|
||||||
{
|
{
|
||||||
audioState = state;
|
audioState = state;
|
||||||
|
log_i("set Audio state %d", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getAudioState(void)
|
bool getAudioState(void)
|
||||||
@@ -111,6 +133,7 @@ void handleAudio()
|
|||||||
if (!mp3->loop())
|
if (!mp3->loop())
|
||||||
{
|
{
|
||||||
mp3->stop();
|
mp3->stop();
|
||||||
|
log_w("Audio: loop");
|
||||||
playSong(audio_current_Song);
|
playSong(audio_current_Song);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +1,58 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef HARDWARE
|
#ifndef HARDWARE
|
||||||
#define HARDWARE 2
|
#define HARDWARE 2
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HARDWARE == 2
|
#if HARDWARE == 2
|
||||||
#define I2S_BCLK 19
|
#define I2S_BCLK 19
|
||||||
#define I2S_WCLK 21
|
#define I2S_WCLK 21
|
||||||
#define I2S_DATA 18
|
#define I2S_DATA 18
|
||||||
#define DAC_SDMODE 23
|
#define DAC_SDMODE 23
|
||||||
|
|
||||||
#define NFC_SS 25
|
#define NFC_SS 25
|
||||||
#define NFC_SCK 14
|
#define NFC_SCK 14
|
||||||
#define NFC_MOSI 26
|
#define NFC_MOSI 26
|
||||||
#define NFC_MISO 27
|
#define NFC_MISO 27
|
||||||
#define NFC_IRQ 13
|
#define NFC_IRQ 13
|
||||||
|
|
||||||
#define PWR_HOLD 4
|
#define PWR_HOLD 4
|
||||||
#define PWR_BTN 5
|
#define PWR_BTN 5
|
||||||
#define MEAS_EN 22
|
#define MEAS_EN 22
|
||||||
|
|
||||||
#define I2C_SDA 33
|
#define I2C_SDA 33
|
||||||
#define I2C_SCL 32
|
#define I2C_SCL 32
|
||||||
#define MEAS_ADC 1 //ADC1_CH7
|
#define MEAS_ADC 1 // ADC1_CH7
|
||||||
|
|
||||||
#define HALL_INPUT 0 //ADC1_CH0
|
#define HALL_INPUT 0 // ADC1_CH0
|
||||||
|
|
||||||
#define LED_PIN 12
|
#define LED_PIN 12
|
||||||
|
|
||||||
|
#define VBATTMIN 3200
|
||||||
|
#define VBATTMAX 4180
|
||||||
|
#define VBATTREF 3300
|
||||||
|
#define R12 4.7
|
||||||
|
#define R13 10
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HARDWARE == 1
|
#if HARDWARE == 1
|
||||||
#define I2S_BCLK 21
|
#define I2S_BCLK 21
|
||||||
#define I2S_WCLK 13
|
#define I2S_WCLK 13
|
||||||
#define I2S_DATA 14
|
#define I2S_DATA 14
|
||||||
#define DAC_SDMODE 27
|
#define DAC_SDMODE 27
|
||||||
|
|
||||||
#define NFC_SS 25
|
#define NFC_SS 25
|
||||||
#define NFC_SCK 18
|
#define NFC_SCK 18
|
||||||
#define NFC_MOSI 23
|
#define NFC_MOSI 23
|
||||||
#define NFC_MISO 19
|
#define NFC_MISO 19
|
||||||
#define NFC_RST 22 //not connectedx
|
#define NFC_RST 22 // not connectedx
|
||||||
#define NFC_IRQ 26
|
#define NFC_IRQ 26
|
||||||
|
|
||||||
#define PWR_HOLD 4
|
#define PWR_HOLD 4
|
||||||
#define PWR_BTN 9
|
#define PWR_BTN 9
|
||||||
#define MEAS_EN 10
|
#define MEAS_EN 10
|
||||||
#define MEAS_ADC 35 //ADC1_CH7
|
#define MEAS_ADC 35 // ADC1_CH7
|
||||||
|
|
||||||
#define HALL_INPUT 36 //ADC1_CH0
|
#define HALL_INPUT 36 // ADC1_CH0
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -63,17 +63,22 @@ void handleConfig(void)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void getConfigSong(const char* uid, const char* filename)
|
String getConfigSong(String uid)
|
||||||
{
|
{
|
||||||
JsonArray array = tagDoc["tags"].as<JsonArray>();
|
JsonArray array = tagDoc["tags"].as<JsonArray>();
|
||||||
|
|
||||||
for (JsonVariant v : array)
|
for (JsonVariant v : array)
|
||||||
{
|
{
|
||||||
const char *taguid = v["TagUID"];
|
String taguid((const char*)v["TagUID"]);
|
||||||
if (!strcmp(uid, taguid))
|
uint16_t result = uid.compareTo(taguid)
|
||||||
|
|
||||||
|
log_v("compare %s(config) with %s(read) = %d",taguid.c_Str(), uid.c_Str(), result);
|
||||||
|
if (!result)
|
||||||
{
|
{
|
||||||
filename = v["audiofile"];
|
String filename((const char*)v["audiofile"]);
|
||||||
|
log_i("Tag found in config, filename = %s", filename.c_str());
|
||||||
|
return filename;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log_e("taguid %s not found",uid );
|
log_e("taguid %s not found",uid.c_str() );
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
void getConfigSong(const char* uid, const char* filename);
|
String getConfigSong(String uid);
|
||||||
|
|
||||||
void initConfig(void);
|
void initConfig(void);
|
||||||
void handleConfig(void);
|
void handleConfig(void);
|
||||||
3
FW/leo_muziekdoos_esp32/src/defines.h
Normal file
3
FW/leo_muziekdoos_esp32/src/defines.h
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
@@ -2,10 +2,6 @@
|
|||||||
|
|
||||||
//#include "math.h"
|
//#include "math.h"
|
||||||
|
|
||||||
uint32_t last_hall_read;
|
|
||||||
uint16_t last_hall_sample;
|
|
||||||
uint8_t hall_idle_count;
|
|
||||||
bool hall_is_Idle = true;
|
|
||||||
bool newState = true;
|
bool newState = true;
|
||||||
uint32_t idleTime = 0;
|
uint32_t idleTime = 0;
|
||||||
|
|
||||||
@@ -103,9 +99,8 @@ void handleGame(void)
|
|||||||
// get filefrom config
|
// get filefrom config
|
||||||
String lastUID = getRFIDlastUID();
|
String lastUID = getRFIDlastUID();
|
||||||
log_i("uid=%s", lastUID.c_str());
|
log_i("uid=%s", lastUID.c_str());
|
||||||
const char* nextSong = "";
|
String nextSong = getConfigSong(lastUID);
|
||||||
getConfigSong(lastUID.c_str(), nextSong);
|
log_i("nextSong=%s", nextSong.c_str());
|
||||||
log_i("nextSong=%s", nextSong);
|
|
||||||
playSong(nextSong);
|
playSong(nextSong);
|
||||||
setAudioState(true);
|
setAudioState(true);
|
||||||
setGameState(statePlaying);
|
setGameState(statePlaying);
|
||||||
@@ -160,49 +155,3 @@ void handleGame(void)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hallIsIdle(void)
|
|
||||||
{
|
|
||||||
return hall_is_Idle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleHallSensor(void)
|
|
||||||
{
|
|
||||||
uint32_t timeNow = millis();
|
|
||||||
if (timeNow - last_hall_read > HALLINTERVAL)
|
|
||||||
{
|
|
||||||
uint16_t hall_sample = getHall();
|
|
||||||
uint16_t hall_delta = (last_hall_sample > hall_sample) ? (last_hall_sample - hall_sample) : (hall_sample - last_hall_sample);
|
|
||||||
if (hall_delta > HALLIDLETHRESHOLD)
|
|
||||||
{
|
|
||||||
if (hall_idle_count > HALLIDLESAMPLES)
|
|
||||||
{
|
|
||||||
hall_is_Idle = false;
|
|
||||||
hall_idle_count = HALLPLAYSAMPLES;
|
|
||||||
log_i("Game: playing");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hall_idle_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (hall_idle_count == 0)
|
|
||||||
{
|
|
||||||
hall_is_Idle = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hall_idle_count--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log_v("HallSensor: val=%d, delta=%d, count=%d, idle=%s\n",
|
|
||||||
hall_sample,
|
|
||||||
hall_delta,
|
|
||||||
hall_idle_count,
|
|
||||||
(hall_is_Idle ? "yes" : "no"));
|
|
||||||
last_hall_sample = hall_sample;
|
|
||||||
last_hall_read = timeNow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,11 +7,6 @@
|
|||||||
#include "rfid.h"
|
#include "rfid.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#define HALLINTERVAL 100
|
|
||||||
#define HALLIDLETHRESHOLD 4
|
|
||||||
#define HALLIDLESAMPLES 4
|
|
||||||
#define HALLPLAYSAMPLES 8
|
|
||||||
|
|
||||||
#define TIMEOUT_IDLE 20000
|
#define TIMEOUT_IDLE 20000
|
||||||
|
|
||||||
typedef enum{
|
typedef enum{
|
||||||
|
|||||||
@@ -22,23 +22,27 @@ void setup()
|
|||||||
|
|
||||||
initStorage();
|
initStorage();
|
||||||
initConfig();
|
initConfig();
|
||||||
//initOta();
|
initOta();
|
||||||
initAudio();
|
initAudio();
|
||||||
initRfid();
|
initRfid();
|
||||||
initSensor();
|
initSensor();
|
||||||
initGame();
|
initGame();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
looptime = millis();
|
looptime = millis();
|
||||||
|
|
||||||
handleAudio();
|
|
||||||
handleRfid();
|
|
||||||
handleSensor();
|
|
||||||
handleGame();
|
|
||||||
handlePower();
|
handlePower();
|
||||||
//handleOta();
|
|
||||||
log_v("main: looptime = %d", millis() - looptime);
|
if (getPowerState() == POWERSTATES::on)
|
||||||
|
{
|
||||||
|
handleAudio();
|
||||||
|
handleRfid();
|
||||||
|
handleSensor();
|
||||||
|
handleHallSensor();
|
||||||
|
handleGame();
|
||||||
|
handleOta();
|
||||||
|
log_v("main: looptime = %d", millis() - looptime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +1,158 @@
|
|||||||
#include "ota.h"
|
#include "ota.h"
|
||||||
|
|
||||||
const char *ssid = SECRET_SSID;
|
bool OtaProcess_class::initialize(void)
|
||||||
const char *password = SECRET_PASS;
|
{
|
||||||
|
if (m_newState)
|
||||||
|
{
|
||||||
|
log_i("Otastate = initialize");
|
||||||
|
m_newState = false;
|
||||||
|
m_otaState = otaInit;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_otaState)
|
||||||
|
{
|
||||||
|
case otaInit:
|
||||||
|
log_i("Otastate = initialize(init)");
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.begin(SECRET_SSID, SECRET_PASS);
|
||||||
|
m_otaState = otaConnect;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case otaConnect:
|
||||||
|
log_i("Otastate = initialize(connect)");
|
||||||
|
|
||||||
|
if (WiFi.waitForConnectResult() != WL_CONNECTED)
|
||||||
|
{
|
||||||
|
log_e("Connection Failed! Retry...");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_otaState = otaSetup;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case otaSetup:
|
||||||
|
{
|
||||||
|
log_i("Otastate = initialize(setup)");
|
||||||
|
// Port defaults to 3232
|
||||||
|
// ArduinoOTA.setPort(3232);
|
||||||
|
// Hostname defaults to esp3232-[MAC]
|
||||||
|
ArduinoOTA.setHostname("muziekdoos");
|
||||||
|
// No authentication by default
|
||||||
|
// ArduinoOTA.setPassword("admin");
|
||||||
|
// Password can be set with it's md5 value as well
|
||||||
|
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
|
||||||
|
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
|
||||||
|
ArduinoOTA
|
||||||
|
.onStart([]()
|
||||||
|
{
|
||||||
|
String type;
|
||||||
|
if (ArduinoOTA.getCommand() == U_FLASH)
|
||||||
|
{
|
||||||
|
type = "sketch";
|
||||||
|
}
|
||||||
|
else // U_SPIFFS
|
||||||
|
{
|
||||||
|
type = "filesystem";
|
||||||
|
LittleFS.end();
|
||||||
|
}
|
||||||
|
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
|
||||||
|
Serial.println("Start updating " + type); })
|
||||||
|
.onEnd([]()
|
||||||
|
{ log_i("End"); })
|
||||||
|
.onProgress([](unsigned int progress, unsigned int total)
|
||||||
|
{ log_i("Progress: %u%%\r", (progress / (total / 100))); })
|
||||||
|
.onError([](ota_error_t error)
|
||||||
|
{
|
||||||
|
log_e("Error[%u]: ", error);
|
||||||
|
if (error == OTA_AUTH_ERROR) log_e("Auth Failed");
|
||||||
|
else if (error == OTA_BEGIN_ERROR) log_e("Begin Failed");
|
||||||
|
else if (error == OTA_CONNECT_ERROR) log_e("Connect Failed");
|
||||||
|
else if (error == OTA_RECEIVE_ERROR) log_e("Receive Failed");
|
||||||
|
else if (error == OTA_END_ERROR) log_e("End Failed"); });
|
||||||
|
|
||||||
|
m_otaState = otaStart;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case otaStart:
|
||||||
|
{
|
||||||
|
log_i("Otastate = initialize(start)");
|
||||||
|
ArduinoOTA.begin();
|
||||||
|
log_i("Ota ready, IPaddress:%s", WiFi.localIP());
|
||||||
|
m_otaState = otaInitDone;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case otaInitDone:
|
||||||
|
{
|
||||||
|
setProcessState(processIdle);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OtaProcess_class::idle(void)
|
||||||
|
{
|
||||||
|
if (m_newState)
|
||||||
|
{
|
||||||
|
log_i("Otastate = Idle");
|
||||||
|
m_newState = false;
|
||||||
|
}
|
||||||
|
if (m_otaState = otaInitDone)
|
||||||
|
{
|
||||||
|
ArduinoOTA.handle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OtaProcess_class::active(void)
|
||||||
|
{
|
||||||
|
if (m_newState)
|
||||||
|
{
|
||||||
|
log_i("Otastate = active(=>idle)");
|
||||||
|
m_newState = false;
|
||||||
|
}
|
||||||
|
idle();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OtaProcess_class::disabled(void)
|
||||||
|
{
|
||||||
|
if (m_newState)
|
||||||
|
{
|
||||||
|
log_i("Otastate = disabled");
|
||||||
|
m_newState = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OtaProcess_class::halted(void)
|
||||||
|
{
|
||||||
|
if (m_newState)
|
||||||
|
{
|
||||||
|
log_i("Otastate = halted(=>disabled)");
|
||||||
|
m_newState = false;
|
||||||
|
}
|
||||||
|
disabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OtaProcess_class::stopped(void)
|
||||||
|
{
|
||||||
|
if (m_newState)
|
||||||
|
{
|
||||||
|
log_i("Otastate = stopped(=>disabled)");
|
||||||
|
m_newState = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WiFi.getMode() != WIFI_MODE_NULL)
|
||||||
|
{
|
||||||
|
WiFi.mode(WIFI_MODE_NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void initOta(void)
|
void initOta(void)
|
||||||
{
|
{
|
||||||
// pinMode(led, OUTPUT);
|
|
||||||
// Serial.begin(115200);
|
|
||||||
// Serial.println("Booting");
|
|
||||||
WiFi.mode(WIFI_STA);
|
|
||||||
WiFi.begin(SECRET_SSID, SECRET_PASS);
|
|
||||||
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
|
||||||
Serial.println("Connection Failed! Retry...");
|
|
||||||
delay(1000);
|
|
||||||
}
|
|
||||||
// Port defaults to 3232
|
|
||||||
// ArduinoOTA.setPort(3232);
|
|
||||||
// Hostname defaults to esp3232-[MAC]
|
|
||||||
ArduinoOTA.setHostname("muziekdoos");
|
|
||||||
// No authentication by default
|
|
||||||
// ArduinoOTA.setPassword("admin");
|
|
||||||
// Password can be set with it's md5 value as well
|
|
||||||
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
|
|
||||||
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
|
|
||||||
ArduinoOTA
|
|
||||||
.onStart([]() {
|
|
||||||
String type;
|
|
||||||
if (ArduinoOTA.getCommand() == U_FLASH)
|
|
||||||
type = "sketch";
|
|
||||||
else // U_SPIFFS
|
|
||||||
type = "filesystem";
|
|
||||||
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
|
|
||||||
Serial.println("Start updating " + type);
|
|
||||||
})
|
|
||||||
.onEnd([]() {
|
|
||||||
Serial.println("\nEnd");
|
|
||||||
})
|
|
||||||
.onProgress([](unsigned int progress, unsigned int total) {
|
|
||||||
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
|
||||||
})
|
|
||||||
.onError([](ota_error_t error) {
|
|
||||||
Serial.printf("Error[%u]: ", error);
|
|
||||||
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
|
|
||||||
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
|
|
||||||
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
|
|
||||||
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
|
|
||||||
else if (error == OTA_END_ERROR) Serial.println("End Failed");
|
|
||||||
});
|
|
||||||
ArduinoOTA.begin();
|
|
||||||
Serial.println("Ready");
|
|
||||||
Serial.print("IP address: ");
|
|
||||||
Serial.println(WiFi.localIP());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleOta(void)
|
void handleOta(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
ArduinoOTA.handle();
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,34 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "ArduinoOTA.h"
|
#include "ArduinoOTA.h"
|
||||||
#include "secrets.h"
|
#include "secrets.h"
|
||||||
// #include <WiFi.h>
|
#include "defines.h"
|
||||||
// #include <AsyncTCP.h>
|
#include "process.h"
|
||||||
// #include <ESPAsyncWebServer.h>
|
|
||||||
// #include <AsyncElegantOTA.h>
|
#include "LITTLEFS.h"
|
||||||
|
|
||||||
|
class OtaProcess_class : public processClass
|
||||||
|
{
|
||||||
|
typedef enum{
|
||||||
|
otaInit,
|
||||||
|
otaConnect,
|
||||||
|
otaSetup,
|
||||||
|
otaStart,
|
||||||
|
otaInitDone,
|
||||||
|
otaError
|
||||||
|
}OTASTATES;
|
||||||
|
|
||||||
|
OTASTATES m_otaState = otaInit;
|
||||||
|
|
||||||
|
void active(void);
|
||||||
|
bool initialize(void);
|
||||||
|
void idle(void);
|
||||||
|
void halted(void);
|
||||||
|
void disabled(void);
|
||||||
|
void stopped(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
OtaProcess_class(uint32_t timeout):processClass(timeout){}
|
||||||
|
};
|
||||||
|
|
||||||
void initOta(void);
|
void initOta(void);
|
||||||
void handleOta(void);
|
void handleOta(void);
|
||||||
@@ -3,66 +3,204 @@
|
|||||||
bool powerbutton_released = true;
|
bool powerbutton_released = true;
|
||||||
|
|
||||||
uint32_t PowerLastKeepAlive = 0;
|
uint32_t PowerLastKeepAlive = 0;
|
||||||
|
uint32_t PowerOtaLongPressTime = 0;
|
||||||
|
uint64_t measure_timer = 0;
|
||||||
|
|
||||||
|
POWERSTATES powerstate = off;
|
||||||
|
|
||||||
|
Button buttonPower(PWR_BTN, 250UL, 1U, 0);
|
||||||
|
|
||||||
|
Battery battery(VBATTMIN, VBATTMAX, MEAS_ADC, &getvbatt);
|
||||||
|
|
||||||
void initPowerOn(void)
|
void initPowerOn(void)
|
||||||
{
|
{
|
||||||
// disable brownout
|
pinMode(PWR_HOLD, OUTPUT);
|
||||||
if (digitalRead(PWR_BTN))
|
powerstate = poweringOn;
|
||||||
{
|
buttonPower.begin();
|
||||||
// enable LDO
|
|
||||||
pinMode(PWR_HOLD, OUTPUT);
|
|
||||||
pinMode(PWR_BTN, INPUT);
|
|
||||||
digitalWrite(PWR_HOLD, HIGH);
|
|
||||||
powerbutton_released = false;
|
|
||||||
|
|
||||||
// dac_sdMode
|
|
||||||
pinMode(DAC_SDMODE, OUTPUT);
|
|
||||||
digitalWrite(DAC_SDMODE, HIGH);
|
|
||||||
// powerstate = poweringOn;
|
|
||||||
// buttonPower.begin();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ESP.deepSleep(10000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void powerDown(uint16_t delayMs)
|
void initBattery(void)
|
||||||
{
|
{
|
||||||
log_w("poweringDown!");
|
battery.onDemand(MEAS_EN, LOW);
|
||||||
digitalWrite(PWR_HOLD, LOW);
|
battery.begin(VBATTREF, (R12 + R13) / R13); // R1 = 220K, R2 = 100K, factor = (R1+R2)/R2
|
||||||
delay(delayMs);
|
|
||||||
ESP.restart();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void initPower(void)
|
POWERSTATES getPowerState(void)
|
||||||
{
|
{
|
||||||
log_i("Power: init");
|
return powerstate;
|
||||||
initPowerOn();
|
|
||||||
log_i("Power: init: done");
|
|
||||||
}
|
|
||||||
|
|
||||||
void handlePower(void)
|
|
||||||
{
|
|
||||||
if (digitalRead(PWR_BTN) && powerbutton_released)
|
|
||||||
{
|
|
||||||
while (digitalRead(PWR_BTN))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
powerDown(1000);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
powerbutton_released = true;
|
|
||||||
}
|
|
||||||
// check timeout
|
|
||||||
if ( millis() - PowerLastKeepAlive > TIMEOUT_POWER)
|
|
||||||
{
|
|
||||||
powerDown(1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PowerKeepAlive(void)
|
void PowerKeepAlive(void)
|
||||||
{
|
{
|
||||||
PowerLastKeepAlive = millis();
|
PowerLastKeepAlive = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void powerOn(void)
|
||||||
|
{
|
||||||
|
digitalWrite(PWR_HOLD, HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void powerOff(void)
|
||||||
|
{
|
||||||
|
log_w("poweringDown!");
|
||||||
|
digitalWrite(PWR_HOLD, LOW);
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool measureBattery(void)
|
||||||
|
{
|
||||||
|
uint16_t vbatt = battery.voltage();
|
||||||
|
if (vbatt < VBATTMIN)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handleBattery(void)
|
||||||
|
{
|
||||||
|
uint64_t currentmillis = millis();
|
||||||
|
if (currentmillis - measure_timer > BATTERYMEASUREDELAY)
|
||||||
|
{
|
||||||
|
return measureBattery();
|
||||||
|
measure_timer = currentmillis;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle power asynchronious
|
||||||
|
void handlePowerState(void)
|
||||||
|
{
|
||||||
|
bool buttonread = buttonPower.read(); // update powerbutton
|
||||||
|
switch (powerstate)
|
||||||
|
{
|
||||||
|
case off:
|
||||||
|
{
|
||||||
|
if (buttonread)
|
||||||
|
{
|
||||||
|
powerstate = poweringOn;
|
||||||
|
}
|
||||||
|
powerOff();
|
||||||
|
log_w("Powered-off");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case poweringOn:
|
||||||
|
{
|
||||||
|
if (buttonPower.pressedFor(POWERBUTTONDELAY))
|
||||||
|
{
|
||||||
|
powerstate = poweringOn2;
|
||||||
|
log_i("poweron 3/3 => Go");
|
||||||
|
}
|
||||||
|
else if (buttonPower.pressedFor(500))
|
||||||
|
{
|
||||||
|
log_i("poweron 2/3");
|
||||||
|
}
|
||||||
|
else if (buttonPower.pressedFor(200))
|
||||||
|
{
|
||||||
|
log_i("poweron 1/3");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
powerstate = off;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case poweringOn2:
|
||||||
|
{
|
||||||
|
if (!buttonread)
|
||||||
|
{
|
||||||
|
powerstate = powerinit;
|
||||||
|
powerOn();
|
||||||
|
// if (measureBattery())
|
||||||
|
// {
|
||||||
|
// log_w("poweringOn: Lowbat");
|
||||||
|
// powerstate = lowBatt;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case powerinit:
|
||||||
|
{
|
||||||
|
// init all
|
||||||
|
log_i("powerinit");
|
||||||
|
powerstate = on;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case on:
|
||||||
|
{
|
||||||
|
if (buttonPower.pressedFor(100))
|
||||||
|
{
|
||||||
|
powerstate = poweringOff;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// if (handleBattery())
|
||||||
|
// {
|
||||||
|
// log_w("on: Lowbat");
|
||||||
|
// powerstate = lowBatt;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case poweringOff:
|
||||||
|
{
|
||||||
|
if (buttonPower.pressedFor(POWERBUTTONDELAY))
|
||||||
|
{
|
||||||
|
powerstate = poweringOff2;
|
||||||
|
log_w("poweringoff: 3/3 ==> powerOff");
|
||||||
|
}
|
||||||
|
else if (buttonPower.pressedFor(500))
|
||||||
|
{
|
||||||
|
log_w("poweringoff: 2/3");
|
||||||
|
}
|
||||||
|
else if (buttonPower.pressedFor(200))
|
||||||
|
{
|
||||||
|
log_w("poweringoff: 1/3");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
powerstate = on;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case poweringOff2:
|
||||||
|
{
|
||||||
|
if (!buttonread)
|
||||||
|
{
|
||||||
|
powerstate = off;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case timeOut:
|
||||||
|
{
|
||||||
|
log_w("timeout ==> off");
|
||||||
|
powerstate = off;
|
||||||
|
powerOff();
|
||||||
|
delay(5000);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case lowBatt:
|
||||||
|
{
|
||||||
|
powerstate = off;
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
log_w("lowbatt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* public functions */
|
||||||
|
|
||||||
|
void initPower(void)
|
||||||
|
{
|
||||||
|
log_i("Power: init");
|
||||||
|
initPowerOn();
|
||||||
|
initBattery();
|
||||||
|
log_i("Power: init: done");
|
||||||
|
}
|
||||||
|
|
||||||
|
void handlePower(void)
|
||||||
|
{
|
||||||
|
handlePowerState();
|
||||||
|
}
|
||||||
@@ -2,8 +2,32 @@
|
|||||||
|
|
||||||
#include "board.h"
|
#include "board.h"
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
|
#include "JC_Button.h"
|
||||||
|
#include "Battery.h"
|
||||||
|
#include "sensor.h"
|
||||||
|
|
||||||
|
|
||||||
#define TIMEOUT_POWER (5 * 1000 * 60) //5minutes timeout
|
#define TIMEOUT_POWER (5 * 1000 * 60) //5minutes timeout
|
||||||
|
#define POWERBUTTONDELAY 1000
|
||||||
|
#define BATTERYMEASUREDELAY 60000
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
off,
|
||||||
|
poweringOn,
|
||||||
|
poweringOn2,
|
||||||
|
powerinit,
|
||||||
|
on,
|
||||||
|
poweringOff,
|
||||||
|
poweringOff2,
|
||||||
|
timeOut,
|
||||||
|
lowBatt
|
||||||
|
} POWERSTATES;
|
||||||
|
|
||||||
|
POWERSTATES getPowerState( void );
|
||||||
|
|
||||||
|
void initBattery(void);
|
||||||
|
bool handleBattery(void);
|
||||||
|
|
||||||
void initPower(void);
|
void initPower(void);
|
||||||
void handlePower(void);
|
void handlePower(void);
|
||||||
49
FW/leo_muziekdoos_esp32/src/process.cpp
Normal file
49
FW/leo_muziekdoos_esp32/src/process.cpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#include "process.h"
|
||||||
|
|
||||||
|
void processClass::run(void)
|
||||||
|
{
|
||||||
|
switch (m_ProcessState)
|
||||||
|
{
|
||||||
|
case processDisabled:
|
||||||
|
disabled();
|
||||||
|
m_initSuccess = false;
|
||||||
|
m_lastRun = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case processInit:
|
||||||
|
initialize();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case processHalt:
|
||||||
|
halted();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case processIdle:
|
||||||
|
idle();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case processActive:
|
||||||
|
|
||||||
|
active();
|
||||||
|
m_lastRun = millis();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case processStopped:
|
||||||
|
stopped();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
m_ProcessState = processDisabled;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void processClass::setProcessState(PROCESS_STATE state)
|
||||||
|
{
|
||||||
|
m_ProcessState = state;
|
||||||
|
m_newState = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PROCESS_STATE processClass::getProcessState(void)
|
||||||
|
{
|
||||||
|
return m_ProcessState;
|
||||||
|
}
|
||||||
38
FW/leo_muziekdoos_esp32/src/process.h
Normal file
38
FW/leo_muziekdoos_esp32/src/process.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
processDisabled,
|
||||||
|
processInit,
|
||||||
|
processHalt,
|
||||||
|
processIdle,
|
||||||
|
processActive,
|
||||||
|
processStopped
|
||||||
|
} PROCESS_STATE;
|
||||||
|
|
||||||
|
class processClass
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
PROCESS_STATE m_ProcessState = processDisabled;
|
||||||
|
bool m_newState = true;
|
||||||
|
bool m_initSuccess = false;
|
||||||
|
uint32_t m_lastRun;
|
||||||
|
const uint32_t mc_timeout;
|
||||||
|
|
||||||
|
virtual void active(void);
|
||||||
|
virtual bool initialize(void);
|
||||||
|
virtual void idle(void);
|
||||||
|
virtual void halted(void);
|
||||||
|
virtual void disabled(void);
|
||||||
|
virtual void stopped(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
processClass(uint32_t timeout) : mc_timeout(timeout) {}
|
||||||
|
|
||||||
|
void run(void);
|
||||||
|
|
||||||
|
void setProcessState(PROCESS_STATE state);
|
||||||
|
PROCESS_STATE getProcessState(void);
|
||||||
|
};
|
||||||
@@ -53,7 +53,7 @@ void setRFIDscanState(bool state)
|
|||||||
|
|
||||||
void clearRFIDlastUID( void)
|
void clearRFIDlastUID( void)
|
||||||
{
|
{
|
||||||
log_i("cleard lasttag");
|
log_i("cleard last tag");
|
||||||
lastUid = "";
|
lastUid = "";
|
||||||
}
|
}
|
||||||
//*****************************************************************************************//
|
//*****************************************************************************************//
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ uint32_t lastVbatt = 0;
|
|||||||
uint16_t BatterySensor = 0;
|
uint16_t BatterySensor = 0;
|
||||||
uint16_t HallSensor = 0;
|
uint16_t HallSensor = 0;
|
||||||
|
|
||||||
|
uint32_t last_hall_read;
|
||||||
|
uint16_t last_hall_sample;
|
||||||
|
uint8_t hall_idle_count;
|
||||||
|
bool hall_is_Idle = true;
|
||||||
|
|
||||||
bool hallinitOK = false;
|
bool hallinitOK = false;
|
||||||
|
|
||||||
void initSensor(void)
|
void initSensor(void)
|
||||||
@@ -60,7 +65,54 @@ uint16_t getHall(void)
|
|||||||
return HallSensor;
|
return HallSensor;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t getvbatt(void)
|
uint16_t getvbatt(uint8_t dummy = 0)
|
||||||
{
|
{
|
||||||
return BatterySensor;
|
return BatterySensor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hallIsIdle(void)
|
||||||
|
{
|
||||||
|
return hall_is_Idle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void handleHallSensor(void)
|
||||||
|
{
|
||||||
|
uint32_t timeNow = millis();
|
||||||
|
if (timeNow - last_hall_read > HALLINTERVAL)
|
||||||
|
{
|
||||||
|
uint16_t hall_sample = getHall();
|
||||||
|
uint16_t hall_delta = (last_hall_sample > hall_sample) ? (last_hall_sample - hall_sample) : (hall_sample - last_hall_sample);
|
||||||
|
if (hall_delta > HALLIDLETHRESHOLD)
|
||||||
|
{
|
||||||
|
if (hall_idle_count > HALLIDLESAMPLES)
|
||||||
|
{
|
||||||
|
hall_is_Idle = false;
|
||||||
|
hall_idle_count = HALLPLAYSAMPLES;
|
||||||
|
log_i("Game: playing");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hall_idle_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (hall_idle_count == 0)
|
||||||
|
{
|
||||||
|
hall_is_Idle = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hall_idle_count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_v("HallSensor: val=%d, delta=%d, count=%d, idle=%s\n",
|
||||||
|
hall_sample,
|
||||||
|
hall_delta,
|
||||||
|
hall_idle_count,
|
||||||
|
(hall_is_Idle ? "yes" : "no"));
|
||||||
|
last_hall_sample = hall_sample;
|
||||||
|
last_hall_read = timeNow;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,9 +7,18 @@
|
|||||||
#define VBATTINTERVALL 1000
|
#define VBATTINTERVALL 1000
|
||||||
#define VBATTMEASPRECHARGE 250
|
#define VBATTMEASPRECHARGE 250
|
||||||
|
|
||||||
|
#define HALLINTERVAL 100
|
||||||
|
#define HALLIDLETHRESHOLD 4
|
||||||
|
#define HALLIDLESAMPLES 4
|
||||||
|
#define HALLPLAYSAMPLES 8
|
||||||
|
|
||||||
|
|
||||||
void initSensor(void);
|
void initSensor(void);
|
||||||
void handleSensor(void);
|
void handleSensor(void);
|
||||||
|
void handleHallSensor(void);
|
||||||
|
|
||||||
|
|
||||||
uint16_t getHall( void );
|
uint16_t getHall( void );
|
||||||
uint16_t getvbatt( void );
|
bool hallIsIdle(void);
|
||||||
|
uint16_t getvbatt(uint8_t dummy);
|
||||||
bool getSensorInitStatus(void);
|
bool getSensorInitStatus(void);
|
||||||
|
|||||||
Reference in New Issue
Block a user