Output Devices


Group assignment

Individual assignment

Learning outcomes:

Have you:


Tools used


Software Used


Files

  1. Eagle files
  2. G-code files
  3. Code files
  4. Datasheet files

Group Assignment

Go to the group assignment page.


Introduction

I started this assignment with the intention of improving the farm irrigation automation. We already have an ESP8266 controlling a pump and 4 relays and monitoring the pump's current draw. The problem is that the ESP8266 with its program is not responsive, and that makes setting up the times time-consuming. This is due to a limitation in the number of ADC pins. It was explained to me by Shakeel, one volunteer that we had on the farm, that the ESP8266 uses its only ADC pin to monitor its wifi connection, so he created a program that reduces the samples taken by the ammeter in order to share the pin with the wifi. Well, this works but makes the wifi connection unreliable, so my challenge will be trying to understand the ESP32 so I can port his program and complete the assignment at the same time. Let's try!

The other thing I would like to point out is that I will focus more on the code, as I already demonstrated the PCB manufacturing process in the other weeks. Also, a major change in the way I complete the assignment is that I will document as I learn and do things, so the assignment will feel more like a log than a tutorial.


Objectives

My initial objective will be to understand the ESP32 better; then, as a second objective, have it replace the current irrigation control; a third objective will be applying all this knowledge to my incubator final project.


Pinout

ESP32 Pinout


Code plan

  1. check time
  2. turn on pump if in time range
  3. read the ammeter looking for under- and overcurrent; if detected, it will stop the pump operation

Code research

I started by checking the website techtutorialsx.com.

As explained on the page, the code to turn on a relay is pretty similar to other Arduino code I have seen, so let's see.

I will be using int pumprelayPin1 = 22; to define the variable pumprelayPin1 as pin 22, and it will store the value as an integer.

pinMode(pumprelayPin1, OUTPUT); sets the pin mode for pumprelayPin1 as an output pin, meaning it will be active, as opposed to an input, which passively monitors for a signal.

digitalWrite(pumprelayPin1, LOW); will set the pin to an off state with the LOW.

delay(4000); will tell the program to wait 4 seconds, or 4000 milliseconds.

That's how my code looks so far:

int pumprelayPin1 = 22;

void setup() {
  
  pinMode(pumprelayPin1, OUTPUT);
  digitalWrite(pumprelayPin1, LOW);

}

void loop() {

  digitalWrite(pumprelayPin1, HIGH);
  delay(4000);
  digitalWrite(pumprelayPin1, LOW);
  delay(4000);

}

So far, this code will start and then turn the relay on and off with 4 seconds between each operation.

Now, for the second part, I want to get the time from a real-time clock in order to activate the pump at certain times. So first I need to include the RTC clock library. I am using a DS1302 RTC clock chip, so this library should do the trick with this code #include <virtuabotixRTC.h>, according to the library documentation here.

Second, we need to declare the pins of the RTC clock with this code virtuabotixRTC myRTC(5, 18, 19); and RtcDS1302<ThreeWire> Rtc(myWire);. This means that when the ESP32 communicates with the real-time clock chip, it will be using pin 5 for CLK, 18 for DAT and 19 for RST.

Warning: RtcDS1302<ThreeWire> Rtc(myWire); comes from a different library (the Rtc library by Makuna, RtcDS1302.h) and myWire is never defined, so that line does not compile as it is. With the virtuabotixRTC library used here, virtuabotixRTC myRTC(5, 18, 19); is all you need.

Now, in order to show the time from the real-time clock, I include this either in the void setup() {} area or in the void loop() {} area. The first will print it once; the second will print it for as long as I have it running. Of course, we need to add a delay of at least a minute with delay(60000);. The unit in the variable is milliseconds, so 1000 means 1 second and 60000 means 60 seconds.

    Serial.begin(115200);

 myRTC.updateTime();
    Serial.println();

If the RTC was never used before, you can set the time by running the following code once: myRTC.setDS1302Time(00, 28, 16, 7, 23, 5, 2020);, the first numbers being the seconds, then minutes, hours (24h mode), day of the week, day of the month, month and year. (Corrected later: the day of the week counts from 1 = Sunday, so Saturday 23/05/2020 is 7 and Sunday 24/05/2020 is 1; the code had 6 for both.)

This is how the code is looking so far:

#include <virtuabotixRTC.h>

virtuabotixRTC myRTC(5, 18, 19);// (CLK,DAT,RST)

int pumprelayPin1 = 22;

void setup() {

    Serial.begin(115200);
    pinMode(pumprelayPin1, OUTPUT);
}
void loop() {

    myRTC.updateTime();
  
    Serial.print(myRTC.hours);
    Serial.print(":");
    Serial.print(myRTC.minutes);
    delay(58000);
  
    digitalWrite(pumprelayPin1, HIGH);
    delay(1000);
    digitalWrite(pumprelayPin1, LOW);
    delay(1000);

}

So far there is no logic test: the code shows the time every minute and turns the relay on and off. So, in order to add a logic test, we need the if statement. It will check if something is true and then execute whatever comes next in the program.

Extracted from the Arduino reference:

if (condition) {
    //statement(s)
}

So, in order to check the time and turn on my relay, I will try something like this: if ((myRTC.hours) == 15 && (myRTC.minutes) == 0) {digitalWrite(pumprelayPin1, HIGH);}

To turn it off, the code will be like this: if ((myRTC.hours) >= 15 && (myRTC.minutes) >= 1) {digitalWrite(pumprelayPin1, LOW);}

My code now looks like this:

//libraries
#include <virtuabotixRTC.h>

//Settings
virtuabotixRTC myRTC(5, 18, 19);// (CLK,DAT,RST)
int pumprelayPin1 = 22;


//run once
void setup() {

   Serial.begin(115200);
   
myRTC.setDS1302Time(50, 15, 16, 1, 24, 5, 2020); // sets seconds, minutes, hours (24h mode), day of the week, day, month, year) comment after first compile
  
  pinMode(pumprelayPin1, OUTPUT);
  
}

//run in loop
void loop() {
  myRTC.updateTime();//updates time
  Serial.print("Current Time: ");
  Serial.print(myRTC.hours);
  Serial.print(":");
  Serial.print(myRTC.minutes);
  Serial.print(":");
  Serial.print(myRTC.seconds);
  Serial.println();
  delay (1000);
//condition (logic test)
if ((myRTC.minutes) == 16) digitalWrite(pumprelayPin1, HIGH);  
if ((myRTC.minutes) >= 17) digitalWrite(pumprelayPin1, LOW);

}

Here is how it runs on the ESP32 development board:

You can see it in higher resolution on YouTube


Board design

For this board I will need at least 4 digital pins, and one analog pin if I want to measure the current of the pump in the future. So I believe the ATtiny44 will be suitable enough. I will leave the ESP32 for the Networking week ;-). Also, my ESP32 SMD components haven't arrived yet; I believe it is because of the COVID-19 break in supply chains. That's why I experimented with the ESP32 development board I had on hand.

Pinout of ATtiny44

ATtiny44 Pinout

With that in hand, I will modify the tiny45 board I made in the Input Devices week by adding another header for more comfortable programming and serial monitoring, as well as all the pins, to have this board as modular as possible.

I won't be covering the Eagle design or milling in detail, as I already did in the Input Devices week. Instead, I will only cover the commands I might not have used, like replace, and then a few hero shots and tests.

So let's start with the redesign!

  1. I start by opening the old Eagle file here.
  2. I tried the replace command, but it did not work to replace the ATtiny45 with the ATtiny44; they are too different, I guess. So I will have to add the new component, remap the connections with net, then name, and then reroute everything on the board. Let's see.

These are the board schematics and layout:

Tiny44 Schematics Schematics

Tiny44 Board LayoutBoard Layout

Next, I run the ULP pcb-gcode, just like explained in Input Devices.

G-code isolation visualizationG-code isolation visualization

Next, I use the program Autoleveller, also explained in Input Devices, and I obtain a G-code that is adjusted to the imperfections of the blank PCB.

Autoleveller outputAutoleveller output

Milling the PCB

The milled PCB

Milled PCBMilled PCB

The ATtiny44 board with the soldered components looks like this.

Populated ATtiny44 BoardPopulated board

Now that I have the PCB done, I changed the code to run on the ATtiny44. One remarkable thing is that the ATtiny44 needs a software serial library, which I invoked with this code #include <SoftwareSerial.h>. Two more things for the serial to work: the library needs to know which pins RX and TX are going to be, SoftwareSerial Monitor(1, 0);; in void setup() {} I included the serial monitor initialization with Monitor.begin(115200); instead of the usual Serial.begin(115200);; and finally, in void loop() {}, every time I wanted the serial to print something, I needed to use the code Monitor.print(); instead of the usual Serial.print();.

In the end my test code looks like this:

//libraries
#include <virtuabotixRTC.h>
#include <SoftwareSerial.h>

//Settings
virtuabotixRTC myRTC(3, 8, 7);// (CLK,DAT,RST)
int pumprelayPin1 = 2;
SoftwareSerial Monitor(1, 0);

//run once
void setup() {

   Monitor.begin(115200);
   
myRTC.setDS1302Time(50, 15, 16, 1, 24, 5, 2020); // sets seconds, minutes, hours (24h mode), day of the week, day, month, year) comment after first compile
  
  pinMode(pumprelayPin1, OUTPUT);
  
}

//run in loop
void loop() {
  myRTC.updateTime();//updates time
  Monitor.print("Current Time: ");
  Monitor.print(myRTC.hours);
  Monitor.print(":");
  Monitor.print(myRTC.minutes);
  Monitor.print(":");
  Monitor.print(myRTC.seconds);
  Monitor.println();
  delay (1000);
//condition (logic test)
if ((myRTC.minutes) == 16) digitalWrite(pumprelayPin1, HIGH);  
if ((myRTC.minutes) >= 17) digitalWrite(pumprelayPin1, LOW);

}

Warning: SoftwareSerial at 115200 baud on the ATtiny44 running on the internal 8 MHz oscillator is unreliable (the timing error garbles the characters). Use Monitor.begin(9600); and set the serial monitor to 9600, which is the safe speed.

Now let's program! For another method without the Arduino IDE, check Embedded Programming.

USBasp programmerUSBasp

Programming an ATtiny44 board with Arduino using the USBasp.

FabISP programmerFabISP

Programming an ATtiny44 board with Arduino using a FabTinyISP or USBtinyISP that I made was fine. There is only the inconvenience of having to power the target board.

Start by opening the Arduino IDE, then in Tools choose the board, in my case ATtiny24/44/84. Next, in Processor, I chose ATtiny44.

Arduino IDE board selection

Next, choose the clock. As I'm not using an external crystal, I chose "Internal 8MHz".

Arduino IDE clock selection

Next, go to Programmer: "USBasp", or "USBtiny" for the FabISP.

Arduino IDE programmer selection

In the video below, I show the process of programming with the FabISP.

For a full-resolution video, check YouTube

In this video, I show the process of programming with the USBasp.

For a full-resolution video, check YouTube

I can show the ATtiny44 controlling a relay to turn on a compressor at a specific time set in the program.

For a full-resolution video, check YouTube

Hero Shot

Hero shot of the boardHero shot


Footnotes

Check usage of millis in the future.

← (Apr 15) Applications and Implications · all weeks · Molding and Casting (Apr 29) →