commit 2ca6ef7017985f621265d6353d39153d4e80f467 Author: Chris Trimble Date: Sat May 18 09:41:44 2024 -0500 Initial commit. Sensors, WiFi, MQTT functional. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..2593a33 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..a6a8a56 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,35 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino +upload_speed = 921600 +upload_port = COM5 +lib_deps = + adafruit/Adafruit VEML7700 Library@^2.1.6 + adafruit/Adafruit INA219@^1.2.3 + jeremycole/I2C Temperature Sensors derived from the LM75@^1.0.3 + knolleary/PubSubClient@^2.8 + +[env:esp32dev_ota] +platform = espressif32 +board = esp32dev +framework = arduino +upload_protocol = espota +upload_port = 10.0.2.10 +upload_flags = + --auth=solar_sensor_array +lib_deps = + adafruit/Adafruit VEML7700 Library@^2.1.6 + adafruit/Adafruit INA219@^1.2.3 + jeremycole/I2C Temperature Sensors derived from the LM75@^1.0.3 + knolleary/PubSubClient@^2.8 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..3772ece --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,250 @@ +/* + Ambient Light Sensor + + Notes: + If lux value changes by more than X ammount, immediately publish + Not as realavant once deep sleep is enabled +*/ +#include +#include +#include +// #include +#include + +#include "Adafruit_VEML7700.h" +#include "Adafruit_INA219.h" +#include +#include + +#define AUX_POWER_ENABLE_PIN (5u) + +// WiFi Setup +const char *ssid = "RAX"; +const char *password = "G9REHYRRY9"; +const IPAddress IP = IPAddress(10, 0, 2, 10); +const IPAddress GW = IPAddress(10, 0, 0, 1); +const IPAddress SN = IPAddress(255, 255, 0, 0); +const IPAddress DNS = IPAddress(10, 0, 0, 1); +const char *hostname = "sensor_array"; + +// Sensor Setup +Adafruit_VEML7700 veml = Adafruit_VEML7700(); +Adafruit_INA219 ina219; +Generic_LM75_12Bit board_temp; + +// MQTT Setup +const char* MQTT_SERVER = "homeassistant.local"; +const uint32_t MQTT_PORT = 1883; +const char* MQTT_USERNAME = "chris"; +const char* MQTT_PASSWORD = "Falcon(91)"; +WiFiClient client; +PubSubClient mqtt(MQTT_SERVER, MQTT_PORT, client); +const char* MQTT_TOPIC = "test/lux"; + +char mqtt_buf[256]; + +void poll_INA219(void); +void init_OTA(void); + +void setup() +{ + pinMode(AUX_POWER_ENABLE_PIN, OUTPUT); + digitalWrite(AUX_POWER_ENABLE_PIN, LOW); + + Serial.begin(115200); + Serial.println("Starting sensor array..."); + + // MCP9800 temp alert pin + pinMode(16, INPUT_PULLUP); + + Wire.begin(23, 22); + if (!veml.begin()) + { + Serial.println("VEML7700 not found."); + } + Serial.println("VEML7700 found."); + + Serial.print(F("Gain: ")); + switch (veml.getGain()) + { + case VEML7700_GAIN_1: + Serial.println("1"); + break; + case VEML7700_GAIN_2: + Serial.println("2"); + break; + case VEML7700_GAIN_1_4: + Serial.println("1/4"); + break; + case VEML7700_GAIN_1_8: + Serial.println("1/8"); + break; + } + + Serial.print(F("Integration Time (ms): ")); + switch (veml.getIntegrationTime()) + { + case VEML7700_IT_25MS: + Serial.println("25"); + break; + case VEML7700_IT_50MS: + Serial.println("50"); + break; + case VEML7700_IT_100MS: + Serial.println("100"); + break; + case VEML7700_IT_200MS: + Serial.println("200"); + break; + case VEML7700_IT_400MS: + Serial.println("400"); + break; + case VEML7700_IT_800MS: + Serial.println("800"); + break; + } + + // ina219 input voltage and current + if (!ina219.begin()) + { + Serial.println("INA219 not found."); + } + Serial.println("INA219 found."); + ina219.setCalibration_16V_400mA(); + + // board temperature sensor + board_temp.setAlertActiveHigh(); + board_temp.setTemperatureHighC(70); + board_temp.setTemperatureLowC(2); + + WiFi.mode(WIFI_STA); + WiFi.config(IP, GW, SN, DNS); + WiFi.setHostname(hostname); + WiFi.begin(ssid, password); + while (WiFi.waitForConnectResult() != WL_CONNECTED) + { + Serial.println("Connection Failed! Rebooting..."); + delay(5000); + ESP.restart(); + } + + ArduinoOTA.setPassword("solar_sensor_array"); + init_OTA(); + ArduinoOTA.begin(); + + if (mqtt.connect("sensor_array", MQTT_USERNAME, MQTT_PASSWORD)) + { + Serial.println("MQTT: Connected to server."); + } +} + +void loop() +{ + static uint32_t prev_millis = 5000; + static uint32_t slow_loop_millis = 60000; + + ArduinoOTA.handle(); + + // MQTT handler + mqtt.loop(); + + if ((millis() - prev_millis) >= 5000) + { + prev_millis = millis(); + + Serial.print("raw ALS: "); + Serial.println(veml.readALS()); + Serial.print("raw white: "); + Serial.println(veml.readWhite()); + Serial.print("lux: "); + Serial.println(veml.readLux(VEML_LUX_AUTO)); + + // poll_INA219(); + + Serial.printf("T = %02.1f\r\n", board_temp.readTemperatureF()); + + if (digitalRead(AUX_POWER_ENABLE_PIN)) + { + digitalWrite(AUX_POWER_ENABLE_PIN, LOW); + } + else + { + digitalWrite(AUX_POWER_ENABLE_PIN, HIGH); + } + } + + if ((millis() - slow_loop_millis) >= 60000) + { + slow_loop_millis = millis(); + + sprintf(mqtt_buf, "{\"lux\":%5.2f,\n\"bus_voltage\":%2.2f,\n\"current_ma\":%3.2f,\n\"temperature\":%2.2f}", + veml.readLux(VEML_LUX_AUTO), ina219.getBusVoltage_V(), ina219.getCurrent_mA(), board_temp.readTemperatureF()); + mqtt.publish(MQTT_TOPIC, mqtt_buf); + } +} + +void poll_INA219(void) +{ + float shuntvoltage = 0; + float busvoltage = 0; + float current_mA = 0; + float loadvoltage = 0; + float power_mW = 0; + + shuntvoltage = ina219.getShuntVoltage_mV(); + busvoltage = ina219.getBusVoltage_V(); + current_mA = ina219.getCurrent_mA(); + power_mW = ina219.getPower_mW(); + loadvoltage = busvoltage + (shuntvoltage / 1000); + + Serial.print("Bus Voltage: "); + Serial.print(busvoltage); + Serial.println(" V"); + Serial.print("Shunt Voltage: "); + Serial.print(shuntvoltage); + Serial.println(" mV"); + Serial.print("Load Voltage: "); + Serial.print(loadvoltage); + Serial.println(" V"); + Serial.print("Current: "); + Serial.print(current_mA); + Serial.println(" mA"); + Serial.print("Power: "); + Serial.print(power_mW); + Serial.println(" mW"); + Serial.println(""); +} + +void init_OTA(void) +{ + 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"); + } }); +} \ No newline at end of file diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html