MPS-Smart-Farm-Controller
Assembling the Mushroom House Controller – Part 2
A Collaboration with Maesai Prasisart School, Measai, Chiang Rai, Thailand
In part two of the project, we let the students assemble the Mushroom house controller that they helped to design. It is important to note that they have never done any of this before, and also that most of the components are SMD.
This made for some interesting moments…
The PCB Arrives
The PCB arrived from the factory, and after an initial inspection by myself, we took it to the school so that a specially selected group of students can try their hand at assembling it.
One of the first remarks by the students was that everything was so tiny… Having never seen SMD assembly, they wrongly assumed that they would be required to manually solder the components using traditional solder and a soldering iron… This feeling of “dismay” was greatly increased when we started laying out the small bags with components.
I choose PCBWay for my PCB manufacturing. Why? What makes them different from the rest?
PCBWay‘s business goal is to be the most professional PCB manufacturer for prototyping and low-volume production work in the world. With more than a decade in the business, they are committed to meeting the needs of their customers from different industries in terms of quality, delivery, cost-effectiveness and any other demanding requests. As one of the most experienced PCB manufacturers and SMT Assemblers in China, they pride themselves to be our (the Makers) best business partners, as well as good friends in every aspect of our PCB manufacturing needs. They strive to make our R&D work easy and hassle-free.
How do they do that?
PCBWay is NOT a broker. That means that they do all manufacturing and assembly themselves, cutting out all the middlemen, and saving us money.
PCBWay’s online quoting system gives a very detailed and accurate picture of all costs upfront, including components and assembly costs. This saves a lot of time and hassle.
PCBWay gives you one-on-one customer support, that answers you in 5 minutes ( from the Website chat ), or by email within a few hours ( from your personal account manager). Issues are really resolved very quickly, not that there are many anyway, but, as we are all human, it is nice to know that when a gremlin rears its head, you have someone to talk to who will do his/her best to resolve your issue as soon as possible.
Find out more here
The Assembly
To make things easier for them, I had previously selected all components, and together with a label and component designators, placed each component into a separate anti-static plastic bag. This achieved two things – it shielded the students from having to handle reels of components, potentially resulting in a lot of wastage, and it make it almost impossible to place a wrong component in a wrong place, as each of the bags were clearly marked with the specific component designator of the component it contained.
Their feelings of “dismay” were quickly replaced with wonder as I used a stencil to apply solder paste to the PCB. There were also confusing present, as they could not understand how the “sticky” solder would melt and keep the component in place. They were also quite worried about placing the components onto the PCB – that was until they saw that there was a selection of fine tweezers set out to use for exactly this purpose…
They now became very excited and took turns to each place a few components onto the PCB. I took special care to keep the diodes, optic isolators and microcontroller well away from them, at least until I explained that these components were polarised, or had to be placed in a specific orientation onto the board.
After a bit of struggling with the diodes, as well as the microcontroller, all the SMD components were eventually correctly placed onto the PCB. I now took over and used a hotplate to reflow the PCB.
This process completely amazed them, or at least, most of them, as some took this opportunity to continue with the ever present interaction of students and mobile phones that are so common in SE Asia 🙂
The PCB was now reflowed, and after a short break to let things cool down, we continued with the soldering and assembly of the through-hole components.
The proceedings would not be complete without a group photo of the students and the PCB that they assembled.
Conclusion of part 2
With the PCB now assembled, I used my desktop CNC machine to cut acrylic plastic to form a protective shell. The PCB will soon be installed at the remote site shown in part 1, and while it will be inside a IP65 electrical enclosure, I still felt the need for a little bit of added protection.
The firmware development is complete, and we are currently busy bringing the students up to date with the exact operation thereof. Our goal is that they would at least try to create their own version of the firmware for use in the electronics lab, as well as a comparison between my version of the firmware and theirs.
From the smiles on their faces during the entire process, it was quite obvious that they really enjoyed this project.
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <DHT_U.h>
/*
* Conditional Defines
*
* These are switches to allow for different versions of the Smart Farm Controller
* to be used with the same firware, as well as to enable/disable serial debugging
* NOTE that deep sleep does NOT FUNCTION CORRECTLY when serial debugging is active.
* THIS IS AN ISSUE WITH THE ESP32S3 AND NOT THE FIRMWARE.
*
*
*/
//#define DEBUG-SERIAL
// Enable below for the 3.3v version of the hardware, IF it has a lipo battery installed
//#define HAS_LIPO
// Enable only is NO lipo battery installed, DONT USE together with HAS_LIPO
#define MAINS_PWR
// ENABLE if the DHT11 Sensor's power is controlled with a transistor.
// This is standard on the 3.3v version, where power saving is important.
#define DHT_SW
// THIS IS THE TIME THAT THE "FOG" relay will allow water to be released
#define FOG_DELAY 10000
// TOP Value of the Humidity Window
#define FOG_HIGH 80
// BOTTOM Value of the Humidity Window
#define FOG_LOW 70
// HOW LONG to Sleep During DEEP SLEEP MODE - 10 Minutes is Maximum, Value is in seconds
#define TIME_TO_SLEEP 600
// HOW LONG TO STAY AWAKE (RUN) - Value is in seconds
#define TIME_AWAKE 300
/*
* PLEASE NOTE THAT THE DEVICE SHALL NOT ENTER DEEP SLEEP IN THE EVENT THAT HUMIDITY LEVELS
* HAVE DROPPED OUTSIDE OF THE ALLOWED WINDOW.
* NORMAL DEEP SLEEP SHALL RESUME WHEN THE HUMIDITY VALUE IS BACK IN THE CORRECT WINDOW
*/
/*
* ===================================================================================================
* ============ DO NOT EDIT BELOW THIS BLOCK - UNLESS YOU KNOW WHAT YOU ARE DOING ====================
* ===================================================================================================
*/
// Variables and Constants
#define DHTPIN 9
#define DHTTYPE DHT11 // DHT 11
#define Relay1 8
#define Relay2 7
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#ifdef DHT_SW
#define DHT_Pin 3 // THIS IS NOT THE DATA PIN, IT IS A CONTROL TRANSISTOR, PRESENT ONLY ON THE 3.3v Version of the controller
#endif
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
const int ledPin = LED_BUILTIN;
int ledState = LOW;
unsigned long previousMillis = 0;
unsigned long previousFog = 0;
uint32_t delayMS;
float temperature = 0;
float humidity = 0;
#ifdef HAS_LIPO
int rawBattVoltage = 0;
float BattVoltage = 0.00;
#endif
bool MAY_FOG = 0;
bool RUN_FOG = 0;
bool DELAY_FOG = 0;
int FOG_LOOP = 0;
RTC_DATA_ATTR int LoopCount = 0;
bool MaySleep = 1;
bool SensorActive = 0;
/*
* CLASS DEFINITIONS FOR EXTERNAL COMPONENTS
*/
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
DHT_Unified dht(DHTPIN, DHTTYPE);
/*
* FUNCTIONS AND PROCEDURES
*/
void print_wakeup_reason(){
#ifdef DEBUG-SERIAL
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
#endif
}
/*
* SETUP FUNCTION - HARDWARE INITIALISATION
*/
void setup() {
//Set relays to initialise as OFF (N/O)
digitalWrite(Relay1,HIGH);
digitalWrite(Relay2,HIGH);
pinMode(Relay1,OUTPUT);
pinMode(Relay2,OUTPUT);
#ifdef DEBUG-SERIAL
Serial.begin(115200);
#endif
LoopCount = 0;
digitalWrite(ledPin,HIGH);
pinMode(ledPin,OUTPUT);
#ifdef DHT_SW
digitalWrite(DHT_Pin,HIGH);
pinMode(DHT_Pin,OUTPUT);
#endif
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
//Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
display.ssd1306_command(SSD1306_DISPLAYON);
display.clearDisplay();
display.display();
delay(20);
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(10,10);
display.print("Booting...");
display.display();
delay(2000);
dht.begin();
#ifdef DEBUG-SERIAL
Serial.println(F("DHT11 Unified Sensor TEST"));
#endif
sensor_t sensor;
dht.temperature().getSensor(&sensor);
#ifdef DEBUG-SERIAL
Serial.println(F("------------------------------------"));
Serial.println(F("Temperature Sensor"));
Serial.print (F("Sensor Type: ")); Serial.println(sensor.name);
Serial.print (F("Driver Ver: ")); Serial.println(sensor.version);
Serial.print (F("Unique ID: ")); Serial.println(sensor.sensor_id);
Serial.print (F("Max Value: ")); Serial.print(sensor.max_value); Serial.println(F("°C"));
Serial.print (F("Min Value: ")); Serial.print(sensor.min_value); Serial.println(F("°C"));
Serial.print (F("Resolution: ")); Serial.print(sensor.resolution); Serial.println(F("°C"));
Serial.println(F("------------------------------------"));
#endif
display.clearDisplay();
display.display();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(10,1);
display.print("Temperature Sensor");
display.setCursor(10,10);
display.print(F("Type: "));
display.print(sensor.version);
display.setCursor(10,20);
display.print(F("Min: "));
display.print(sensor.min_value);
display.print(F(" Deg C"));
display.setCursor(10,30);
display.print(F("Max: "));
display.print(sensor.max_value);
display.print(F(" Deg C"));
display.setCursor(10,40);
display.print(F("Resolution: +/- "));
display.setCursor(10,50);
display.print(sensor.resolution);
display.print(F(" Deg C"));
display.display();
delay(4000);
// Print humidity sensor details.
dht.humidity().getSensor(&sensor);
#ifdef DEBUG-SERIAL
Serial.println(F("Humidity Sensor"));
Serial.print (F("Sensor Type: ")); Serial.println(sensor.name);
Serial.print (F("Driver Ver: ")); Serial.println(sensor.version);
Serial.print (F("Unique ID: ")); Serial.println(sensor.sensor_id);
Serial.print (F("Max Value: ")); Serial.print(sensor.max_value); Serial.println(F("%"));
Serial.print (F("Min Value: ")); Serial.print(sensor.min_value); Serial.println(F("%"));
Serial.print (F("Resolution: ")); Serial.print(sensor.resolution); Serial.println(F("%"));
Serial.println(F("------------------------------------"));
// Set delay between sensor readings based on sensor details.
#endif
display.clearDisplay();
display.display();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(10,1);
display.print("Humidity Sensor");
display.setCursor(10,10);
display.print(F("Type: "));
display.print(sensor.version);
display.setCursor(10,20);
display.print(F("Min: "));
display.print(sensor.min_value);
display.print(F(" % RH"));
display.setCursor(10,30);
display.print(F("Max: "));
display.print(sensor.max_value);
display.print(F(" % RH"));
display.setCursor(10,40);
display.print(F("Resolution: +/- "));
display.setCursor(10,50);
display.print(sensor.resolution);
display.print(F(" % RH"));
display.display();
delay(4000);
delayMS = sensor.min_delay / 1000;
print_wakeup_reason();
#ifdef HAS_LIPO
rawBattVoltage = analogRead(A0);
BattVoltage = ((rawBattVoltage * 2) * (3.34 / 4095.00));
if (BattVoltage > 4.05) {
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
} else if ((BattVoltage > 3.20) && (BattVoltage < 4.20)){
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
} else {
// Below 3.2 volt we consider the battary too low for operation.
// We do not set a wakeup time for deepsleep.
// This will cause the ESP32 to go to sleep and not wakeup until manual reset
}
#endif
#ifdef MAINS_PWR
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
#endif
}
/*
* MAIN LOOP
*/
void loop() {
#ifdef DHT_SW
SensorActive = digitalRead(DHT_Pin);
#else
SensorActive = 1;
#endif
unsigned long currentMillis = millis();
unsigned long FogMillis = millis();
// 10 second "FOG" loop
if (FogMillis - previousFog >= FOG_DELAY) {
previousFog = FogMillis;
if ((digitalRead(Relay1) == LOW) && (RUN_FOG == 1) && (DELAY_FOG == 0)) {
digitalWrite(Relay1,HIGH);
DELAY_FOG = 1;
#ifdef DEBUG-SERIAL
Serial.println(F("RELAY OFF AFTER 10 SEC"));
#endif
} else if (DELAY_FOG == 1) {
DELAY_FOG = 0;
if (RUN_FOG == 1) RUN_FOG = 0;
#ifdef DEBUG-SERIAL
Serial.println(F("RELAY DELAY OFF FOR 10 SEC"));
#endif
}
}
// Main 1 second loop - Non blocking
if (currentMillis - previousMillis >= delayMS) {
// save the last time you blinked the LED
previousMillis = currentMillis;
++LoopCount;
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
// TEST RELAY2
digitalWrite(Relay2,!digitalRead(Relay2));
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
#ifdef HAS_LIPO
rawBattVoltage = analogRead(A0);
BattVoltage = ((rawBattVoltage * 2) * (3.34 / 4095.00));
#endif
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
sensors_event_t event;
dht.temperature().getEvent(&event);
if (isnan(event.temperature)) {
#ifdef DEBUG-SERIAL
Serial.println(F("Error reading temperature!"));
#endif
}
else {
display.setCursor(10,1);
display.setCursor(10,1);
display.print(event.temperature);
display.setCursor(80,1);
display.println(F(" C"));
temperature = event.temperature;
}
// Get humidity event and print its value.
dht.humidity().getEvent(&event);
if (isnan(event.relative_humidity)) {
#ifdef DEBUG-SERIAL
Serial.println(F("Error reading humidity!"));
#endif
}
else {
#ifdef DEBUG-SERIAL
Serial.print(F("Humidity: "));
Serial.print(event.relative_humidity);
Serial.println(F("%"));
#endif
display.setCursor(10,20);
display.print(event.relative_humidity);
display.setCursor(80,20);
display.println(F(" %"));
humidity = event.relative_humidity;
}
#ifdef HAS_LIPO
display.setTextSize(1);
display.setCursor(10,40);
display.print("LiPo ");
display.print(String(BattVoltage));
display.print("v ");
if (BattVoltage >= 4.00)
{
display.println("Charging");
} else if ((BattVoltage >= 3.30) and (BattVoltage <= 4.00)) {
display.println("Batt PWR");
} else {
display.println("LOW BATT");
}
#endif
#ifdef DEBUG-SERIAL
Serial.println("=== TEST DATA ===");
Serial.print("TEMP ");
Serial.print(String(temperature));
Serial.println(F("°C"));
Serial.print("HUMIDITY ");
Serial.print(String(humidity));
Serial.println(F(" %"));
#ifdef HAS_LIPO
Serial.print("LiPo Battery Voltage : ");
Serial.print(BattVoltage);
Serial.println("v ");
Serial.println();
#endif
Serial.print("Time to sleep :");
Serial.print(String(TIME_AWAKE - LoopCount));
Serial.println(" seconds");
Serial.println("=== END ===");
#endif
display.setTextSize(1);
if ((MaySleep == 1) && (RUN_FOG == 0)) {
display.setCursor(10,50);
display.print("DEEPSLEEP in ");
display.print(String(TIME_AWAKE - LoopCount));
display.print("sec ");
} else if ((MaySleep == 0) && (RUN_FOG == 0)){
display.setCursor(10,50);
display.print("DISP OFF in ");
display.print(String(TIME_AWAKE - LoopCount));
display.print("sec ");
}
}
display.display();
if (LoopCount >= TIME_AWAKE)
{
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
if ((MaySleep == 1) && (RUN_FOG ==0)) {
display.setCursor(10,10);
display.print("Going to sleep...");
display.display();
} else {
display.setCursor(10,10);
display.print("Dimming Display...");
display.display();
}
delay(2000);
display.clearDisplay();
display.display();
#ifdef DEBUG-SERIAL
Serial.flush();
#endif
#ifdef DHT_SW
digitalWrite(DHT_Pin,LOW);
#endif
if (digitalRead(Relay1) == LOW) digitalWrite(Relay1,HIGH);
if (digitalRead(Relay2) == LOW) digitalWrite(Relay2,HIGH);
display.ssd1306_command(SSD1306_DISPLAYOFF);
delay(2000);
if ((MaySleep == 1) && (RUN_FOG ==0)){
esp_deep_sleep_start();
}
}
// we only care about readings if and when the sensor is active AND we have valid data...
if (SensorActive == 1) {
if ((humidity < FOG_LOW) && (RUN_FOG == 0)) {
RUN_FOG = 1;
MAY_FOG = 1;
#ifdef DEBUG-SERIAL
Serial.println(F("HUMIDITY BELOW 70"));
#endif
} else if ((humidity >= FOG_LOW) && (humidity <= FOG_HIGH)) {
RUN_FOG = 0;
MAY_FOG = 0;
if (digitalRead(Relay1) == LOW) digitalWrite(Relay1,HIGH);
#ifdef DEBUG-SERIAL
Serial.print("Humidity in Range ");
Serial.print(String(FOG_LOW));
Serial.print(" % to ");
Serial.print(String(FOG_HIGH));
Serial.println(" %");
#endif
}
}
if ((digitalRead(Relay1) == HIGH) && (RUN_FOG == 1) && DELAY_FOG == 0) digitalWrite(Relay1,LOW);
if ((DELAY_FOG == 1) && (MAY_FOG ==1 )) digitalWrite(Relay1,HIGH);
}
MPS-Smart-Farm-Controller
*PCBWay community is a sharing platform. We are not responsible for any design issues and parameter issues (board thickness, surface finish, etc.) you choose.
- Comments(0)
- Likes(2)
- Anatoliy K Jan 26,2024
- RichardV31 Dec 30,2023
- 0 USER VOTES
- YOUR VOTE 0.00 0.00
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
More by Jean Redelinghuys MakerIoT2020
- PCB_MCP23008_2023-10-08 MCP23008 BreakoutI designed this breakout to assist me during prototyping my next version of the “RP...
- PCB_XiaoRP2040-Mouse-REV2 Xiao RP2040 Joystick Mouse – revision 2.00Revision 1.0 of the ProjectOver the last few months, I hav...
- Multi Purpose IO Card Multi-Purpose IO CardWhen we are working on a prototype, we always need access to pushbuttons, encod...
- Variable Voltage Power Module Variable Voltage Power ModulePowering electronics projects are always challenging. This Variable vol...
- I2C Matrix Keypad An I2C Matrix KeypadThe completed I2C Matrix KeypadIn a previous post this month I introduced my 4×4...
- ESP32-S Development Board, in "Arduino Uno" form factor UPDATE 24/06/2023:This board now has a Hardware Revision 2.0 available. It is the same board but wit...
- W307186ASC94_Gerber_PCB_USB-Ports USB Power Supply ModuleUSB Ports are quite handy to power all our day-to-day electronic devices, but...
- Atmega 328P based PWM controller Card ATMega 328P Based PWM controller CardAs part of my recent ESP-12E I2C Base Board project, I designed...
- W307186ASC71_Gerber_PCB_ESP-Now Remote Today we will look at the remote control unit for the Robotic Toy Car – Part 6.The project is close ...
- W307186ASV69_Gerber_PCB_Robot-Car-MCU-Board Prototype In our last project, we started working on repurposing an old toy car. In this part, Robot Toy Car –...
- W307186ASV62_Gerber_PCB_DUAL-H-Bridge by makeriot2020 on May 27, 2022Many of us have old toys laying around the house, they belong to ou...
- CAN-BUS Breakout Breadboard Compatible CAN-BUS Breakout ModuleWhat is this:Some of us have already used the commonly ...
- RA-02 Breakout with Level converters Breadboard and beginner-friendly RA-02 Breakout ModuleMost Makers and electronics enthusiasts may al...
- ATMEGA328P Module with integrated LoRa and CAN Bus ATMEGA328P Module with integrated LoRa and CAN-BUSINTRODUCTIONIn my quest to perfect my LoRa telemet...
- Sx127x-Ra-02-Test-Module with ATMEGA328P-AU SX127x LoRa/FSK/OOK Prototype Radio BoardI recently had a requirement to do some automation/telemetr...
- USB-ASP Programmer ATMEGA8 Build your own USB-ASP Programmer CloneBymakeriot2020 FEB 21, 2022 Arduino, ASP programmerUsing mor...
- ATTiny1616-LIGHT-Controller-with-CAN_B_PCB_ATTiny1616-LIGHT-Controller-with-C_2024-09-11 Assembly of the ATTiny1616 Can bus controller PCBThe Assembly of the ATTiny1616 Can Bus Controller P...
- ATTiny1616QFN-CAN-Remote-Neopixel-Ligh_PCB_ATTiny1616QFN-CAN-Remote-Neopixel-2024-09-11_2024-09-11 NeoPixel CAN-Bus Module with local controlAs part of my current project to add NeoPixels to the cabi...
-
-
-
3D printed Enclosure Backplate for Riden RD60xx power supplies
46 0 0 -
-
-
-
Sega Master System RGB Encoder Switcher Z80 QSB v1.2
53 0 0 -
18650 2S2P Battery Charger, Protection and 5V Output Board
67 0 0 -
High Precision Thermal Imager + Infrared Thermometer | OpenTemp
389 0 6 -
Sony PlayStation Multi Output Frequency Oscillator (MOFO) v1
121 0 2