KalyanChakravarthy.net

Thoughts, stories and ideas.

Automated Plant Watering System - Part 3 - the software

This is a follow up to Part 2 and Part 1 of my Mark2 design and build of Plant watering system.

Learning

  1. The 3.7v LiPo was unable to deliver the required current of about 350 mA for running 2 motors simultaneously. This lead to the batteries dying out prematurely while being perfectly capable of squeezing out juice for just one motor.
  2. Different plants have different water consumption characteristics. For example with the current setup my Basil plant was thriving while the Coriander plant withered away due to over-watering
  3. The use of a push button without a pull down resistor makes it unusable and therefore less useful.

Fortunately the first two learnings can be incorporated by some software changes. Following are the goals I set out to accomplish in the new firmware and the code to achieve it.

Goals

  1. Drive motors independently for different "ON" and interval duration.
  2. Drive motors sequentially, for overlapping run-times/run-intervals. The assumption is its ok if a run is delayed by few seconds.
  3. Support for multiple motors to make the code re-usable for Mark3 prototype.

Solution

  1. Motors are configured by the use of an array of MotorConf structs.
  2. whosNext() computes which motor is to be activated. It does so by checking if the delta time from last run exceeded the runtime+waittime.
  3. The watchdog timer is set to wake up the loop every 1 second. However the clock is prescaled to 1/16th the speed. The combined effect should reduce further power consumption.
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/power.h>

#define adc_disable() (ADCSRA &= ~(1<<ADEN)) // disable ADC (before power-off)
#define adc_enable()  (ADCSRA |=  (1<<ADEN)) // re-enable ADC

#define led 4
#define MPIN_1 1
#define MPIN_2 0
#define MPIN_EXT_1 3
#define MPIN_EXT_2 2

#define setHigh(pin) pinMode(pin, OUTPUT); digitalWrite(pin, HIGH);
#define setLow(pin) pinMode(pin, OUTPUT); digitalWrite(pin, LOW);

unsigned int lpCntr=0;
unsigned int isOn=0;

struct MotorConf {
  byte pin;
  byte onInterval;
  unsigned short offInterval;
  unsigned long lastUsed;
}


const unsigned int offInterval_2hours = 2 * 60 * 60; //(60 * 60 * 6) - onInterval; //round it off to 6 hours
const unsigned int offInterval_4hours = 4 * 60 * 60; //(60 * 60 * 6) - onInterval; //round it off to 6 hours
const unsigned int onInterval_2secs = 2; // 2 sleep cycles
const unsigned int repInterval = onInterval_2secs + offInterval_2hours;


struct MotorConf motorState[] = {
  { 1, 2, offInterval_2hours, 0 },
  { 0, 2, offInterval_4hours, 0 },
}


unsigned int currMotor = -1;
unsigned int maxMotors = 2;

void setup() {
  clock_prescale_set(clock_div_16);

  for(int i=0; i<6; i++) {
    pinMode(i, INPUT);
    digitalWrite(i, LOW);
  }

  pinMode(led, OUTPUT);
  for(int i=0; i<2; i++) {
    digitalWrite(led, HIGH);
    delay(50);
    digitalWrite(led, LOW);
    delay(50);
  }
  digitalWrite(led, LOW);

  adc_disable();
  wdt_reset();          //watchdog reset
  wdt_enable(WDTO_1S);  //1s watchdog timer
  WDTCR |= _BV(WDIE);   //Interrupts watchdog enable
  sei();                //enable interrupts
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
}

int whosNext() {
  unsigned long currTime = millis() / 1000;
  for(int i=0; i<maxMotors; i++) {
    struct MotorConf currConf = motorState[i];
    if( (currTime - currConf.lastUsed) > (currConf.onInterval+currConf.offInterval) ) {
      return i;
    }
  }
  return -1;
}

void loop() {
  // Main loop

  int nextMotor = whosNext();

  if( nextMotor >= 0 && nextMotor <= maxMotors ) {
    struct MotorConf currConf = motorState[i];
    setHigh(currConf.pin);
    delay(currConf.onInterval);
    setLow(currConf.pin);
    currConf.lastUsed = millis() / 1000;
  }

  sleep_enable();
  sleep_cpu();

}

ISR (WDT_vect) {
  WDTCR |= _BV(WDIE);
}

Future improvements

Mark 2 provided fair amount of learnings. However it was plagued with limitations. Here are some improvmenets planned in Mark 3 re-design

  • Use ESP8266 along with MCP23017 GPIO Expander.
    • ESP8266 will aid in improving the configurability as well as observability.
    • MCP23017 I2C GPIP Expander will dramatic increase in number of IO ports to 16.
  • Ports for 8x Motors, 8x Soil input sensors
  • NodeMCU compatibility along with support for ESP8266 12-E bare IC. The former helps with programming, the later with power consumption.

Starlink satellite tracker

This is my first app that is published on both Android Play Store & Apple Appstore platforms. Despite React Native's brittleness and shortcomings, the productivity I experienced while working with it was phenomenal. I think I might just migrate all my old "pure" Objective-C apps to RN.

Really happy with how it turned out. Am even proud of what my lackluster design skills managed to churn out. The apps were built as a companion to the web version that my friend built (me.cmdr2.org/starlink)

What do you think?

Getting rid of Alternate Screen

... or simply making the damn terminal content persist when I seek any form of --help or run git diff.

I don't know whoever thought or why they thought that alternate screen was a sane idea is beyond me. I'd do a git diff to check my commits and promptly forget the specifics by the time I get to writing the commit message. Same with the python REPL's help function. The frustration was the kind that builds up like a thousand paper cuts, each seemingly an insignificant occurrence, only day you suddenly find yourself quadriplegic. It doesn't make any sense.

By changing OSX's terminal from xterm-256 to vt100 (Preferences → Profiles → Advanced → 'declare terminal as') I have been delivered into the sweet arms of salvation and from inflicting cuddles of frustration on my cat that has been patient (for a cat) with me through this ordeal.

Am writing this down here because I seem to run into this every few years and promptly forget how I ever managed to get rid of this pest. No more alternate screen, no more, not until this blog and OneNote chose to go away atleast.

Update 1: Oh god, that was terrible in its own right with vim becoming color blind, which sent down on yet another wild goose chase. So the next solution is to configure the pager, which you better hope is less everywhere

export LESS="-iMFXRj4a"

Now the side effect has been magical of man pages and --help now showing up line numbers! I'd call this a success (for now).

Reference and further reading

  1. http://www.shallowsky.com/linux/noaltscreen.html
  2. https://hints.macworld.com/article.php?story=20110905185128781
  3. https://superuser.com/questions/136162/how-can-i-still-see-the-man-text-after-i-quit-man/136167#136167

Stuff I learnt in 2019

I enjoy learning new things, especialy if I am able to either build something out of it or put it to use to improve my life. Here are some things from the top of my head that I managed to learn or start learning in 2019.

Tech/Programming
Learning new technologies is a never-ending endeavour. I built at least one public-facing hobby project starting from zero with the following:

  • React Native for building iOS & Android apps
    • Published an iOS app with another in the works.
  • Django
    • Built a personal portal to track who I meet, when and how often
  • Dokku
    • How to setup and deploy projects with Dokku
  • VueJS
    • Built few personal utilities using Vue. This completely changed how I view JS
  • Basics of modern JS (ES6)
    • While I wrote a fair amount of Javascript a decade ago, revisiting it made me realise its an entirely new world. Touch basing this has been an uphill battle.
  • Build automation with fastlane
    • Android & iOS
    • React-Native apps

Technology

  • Home security
  • Home automation
  • Using VPNs
  • Securing browser stack

Electronics
Built at least one working thing with each of the following

  • PCB Design
    • Design with KiCad
    • Auto-routing
  • Design for manufacturing
    • BOMs
  • SMD soldering
  • ESP8266s / NodeMCU
    • Programming
    • Components on NodeMCU/Lolin boards
    • Using bare ESP8266
  • ATTiny85
    • Using deep sleep mode
  • Simple Diodes
  • Voltage dividers
  • Voltage regulators
  • Voltage step-up circuits
  • PIR sensors circuits

Personal

  • Swimming!
    • Dutch A,B & C diplomas.
  • Health
    • Slowing down and self-care
    • Mental health
  • Taking care of a cat

Food & Cooking
I think I can make a reasonably decent meal out of many of these without having to look at a recipe while making-do in case of missing ingredients.

  • Pasta
    • Creamy Mushroom Pasta
    • Aglio olio
    • Cacio e Pepe
  • Mushroom Risotto
  • Fried rice with mushrooms
  • Fried noodles/Chow mein (with mushrooms)
  • Chinese Noodles
  • Palak paneer (the hand blender way)
  • Spice blends
    • Blending spice mixes for palak paneer & paneer butter masala
  • Soups
    • Bell pepper soup
    • Tomato soup

Automated Plant Watering System - Part 2 - PCB Design and Prototyping

After running the Mark-1 prototype for a week, I wanted to scale up this effort for more of my plants. However efforts to prototype on a PCB blank were a complete mess, so I decided to jump the gun and build Mark-2.

DSC05718

IMG_0749

Mark-2 Design Goals:

It was clear that running one MCU per plant was not effective, at the same time I wanted configurability and thus compromises had to be made.

  1. Run on AA batteries or 3.7v LiPo cells.
  2. Control at least 2 Motors
  3. Configure using 2 push buttons with ability to bypass it to use other peripherals such as a soil sensor.
  4. Accommodate both Attiny85 bare IC as well as Digispark.

Bill of materials:

  1. Digispark Attiny85 or bare IC.
  2. 2x NPN Transistors (I used BC547 at hand)
  3. 2x Diodes (I used LEDs due to lack of proper diodes)
  4. 2x 3v Submersible DC Pumps
  5. 1x LED for feedback display
  6. 1x battery pack (min 3v)

Design

Design was fairly similar to earlier prototype with just few more ports.

The design phase involved both schematic design as well as PCB footprint design using KiCad. I made use of hierarchical sheets to "re-use" the schematic design for a single motor.

SchematicSummary

I used the "Save/Restore Layout" plugin to do the same in PCB mode.

pcb-summary

Program

It turned out that the use of a push button without a pull down resistor makes it unusable, due to which am now relying on manual programming.

The duration is programmed to be 2 seconds of watering for every 2 hours, which appears to be adequate for my basil and coriander plants

The code itself was similar to earlier version.

PCB Fabrication

The final step was to export Gerber files, containing Image/SVG representation of all individual layers such as copper front and back, silkscreen for info, soldermask, vias, etc.

A key element of design that I missed during first iteration was the edge cuts which is the SVG layer outlining how the PCB has to be cut.

All the exported files were zipped and sent to pcbway. The fabbed PCBs arrived less than a week later. As shipping was the most expensive part of the process, I might perhaps combine multiple designs into a PCB order so they arrive together.

KiCad Note:

Install plugins by copying files from

  • Source: https://github.com/MitjaNemec/Kicad_action_plugins.git
  • Destination (macOS): /Applications/KiCad/kicad.app/Contents/SharedSupport/scripting/plugins/