Wednesday, February 22, 2023

0000 0000 1011 1110

Mailbag #29

There has been a bit of chatter on the channel recently about me NOT providing links and details regarding the items that make up the mailbag videos. 

Here are my reasons:

1. The mail you see me open is 3-6 months old! That's right, I'm still hugely in catch up mode after the insanity buying sprees of 2022. This means the information would most likely be out of date.

2. I shop pretty much 100% on AliExpress. The shops come and go, and often I look back for an item only to find it does not exist on the shop anymore, or even that the whole shop is missing!

3. The items I order are dodgy as anything. I have a limited income stream and so I deliberately choose the cheapest items I can - often they are fake, used or simply not fit for purpose. I love this about shopping online at AliExpress - it's got a real "life is like a box of chocolates" feel about it, but I would not feel comfortable pointing people to these items.

Finally, I nearly always do a screenshot on the video for each item so that you can use these search terms on your favourite site to track down a similar item. If I fail to do that, please let me know in the comments section.

Having said all of that - onwards to the silliness that is Mailbag #29.



Thursday, February 16, 2023

0000 0000 1011 1101

Tiny ESP-01s smashes Motor, LEDs, buzzer!

I love the idea of a small micro-controller "punching" above it's weight. An anaemic tiny processor bossing around motors when it has no right to is my idea of heaven. OK, so I don't get out much.

AND...if you add a little iOT into the recipe, then I'm there for the "fun".

So it happens that we are looking at a gate opening/closing mechanism for the bottom of the driveway.

You can buy them commercially and that's fine for regular sane people - but what about using a tiny ESP-01S to act as an Access Point, run a web server and turn on and off stuff WAY beyond it's capacity given it's flash memory and GPIO limitations?

Sounds like a little experimentation might be required to tip the balance from BUY to DIY.

Yes please!

This project has some distinct phases:

  • run the 3.3V ESP-01s and a heavy inductive motor (24V) from the same power supply
  • extend the ESP-01s meagre output capacity to be able to address many GPIOs using a 74HC595N shift register
  • use a ULN2803 with it's inbuilt flyback diode to handle the motor and it's current requirement
  • make an Access Point to activate the gate from any WiFi capable device

Let's go!

The power issue was solved (as I've done before) with a combination of a buck/boost module (LM2596 based) and LM1117 3.3V linear voltage regulator. The final drop was 20.5V to 7.5V using the switching regulator and then 7.5V to 3.3V using a TO-220 format LM1117 regulator.

I kept calling this an LM117 regulator in the video - apologies!

With this rig the "top" rail is 20.5V (for the motor) and the "bottom" rail is a steady 3.3V. One hilarious event during testing was the destruction of a 16V rated capacitor I forgot to remove which was still sitting in the top rail when I upped the voltage - love that smell of magic smoke!

The 74HC595N shift register part of the project was fairly easily achieved given that I have worked with these units earlier on this blog.

I will post the code below so that you can see just how simple this stuff can be using standard, mostly "borrowed", code.

//Pin connected to latch pin (ST_CP) of 74HC595
const int latchPin = 0;
//Pin connected to clock pin (SH_CP) of 74HC595
const int clockPin = 1;
////Pin connected to Data in (DS) of 74HC595
const int dataPin = 2;

byte shiftbyte = 0;

void setup() 
{
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);  
  pinMode(clockPin, OUTPUT);
}

void loop() {
for (byte countleds = 1; countleds < 5; countleds++) {
  shiftbyte = 0;
  updateShiftRegister(); 
  delay(300);
  bitSet(shiftbyte, countleds);
  updateShiftRegister();
  delay(100);   
}
  shiftbyte = 0;
  updateShiftRegister(); 
  delay(300);
  shiftbyte = 0b00011110;
  updateShiftRegister();
  delay(300);  
}

void updateShiftRegister()
{
   digitalWrite(latchPin, LOW);
   shiftOut(dataPin, clockPin, MSBFIRST, shiftbyte);
   digitalWrite(latchPin, HIGH);
}

Next up was the ULN2803 component - this is a very handy darlington transistor array that comes complete with a flyback diode (marked in the diagram below with a red arrow and circle) to hopefully prevent damage to the controller of an inductive load - e.g. let's not fry the little ESP8266 with a big nasty kick back from the motor when it stops!

We have also seen this component in action before.

Finally, I coded up the ESP-01S so that it behaved as a WiFi Access Point and could, er, "open the gate" complete with warning lights and buzzers. Very exciting!

/*
   Using an ESP-01s to turn on a motor, as
   well as running a few lights and buzzers
   all via a 74HC595N and ULN28703

   OneCircuit Sun 12 Feb 2023 19:52:25 AEDT

*/

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

/* Put your SSID & Password */
const char* ssid = "GateControl";  // Enter SSID here
const char* password = "1234";  //Enter Password here

//Pin connected to latch pin (ST_CP) of 74HC595
const int latchPin = 0;
//Pin connected to clock pin (SH_CP) of 74HC595
const int clockPin = 1;
////Pin connected to Data in (DS) of 74HC595
const int dataPin = 2;

byte shiftbyte = 0; // the state of the shift register

long motor = 0;
boolean motorun = false;
bool GATEstatus = false;
bool openclick = false;
long currenttime = 0;
int turnoffmotortime = 5000;

/* Put IP Address details */
IPAddress local_ip(192, 168, 2, 1);
IPAddress gateway(192, 168, 2, 1);
IPAddress subnet(255, 255, 255, 0);

ESP8266WebServer server(80);

void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  delay(20);
  // clear register
  shiftbyte = 0b00000000;
  updateShiftRegister();
  delay(500);

  WiFi.softAP(ssid, password);
  WiFi.softAPConfig(local_ip, gateway, subnet);
  delay(100);

  server.on("/", handle_OnConnect);
  server.on("/gateon", handle_gateon);
  server.on("/gateoff", handle_gateoff);
  server.onNotFound(handle_NotFound);

  server.begin();
}

void turnonsequence() {

  // turn off green
  shiftbyte = 0b00000000;
  updateShiftRegister();

  // turn on red and buzzer
  shiftbyte = 0b00100100;
  updateShiftRegister();
  delay(3000);
  shiftbyte = 0b00000000;
  updateShiftRegister();

  // flashing yellow, buzzer and motor

  motorun = true;
  motor = millis(); // start timing

  while (motorun) {

    shiftbyte = 0b01001000;
    updateShiftRegister();
    delay(200);
    shiftbyte = 0b01110000;
    updateShiftRegister();
    delay(200);
    shiftbyte = 0b01000000;
    updateShiftRegister();
    currenttime = millis();
    if (currenttime - motor > turnoffmotortime) {
      motorun = false;
    }
  }
  shiftbyte = 0b00000000;
  updateShiftRegister();
  GATEstatus = !GATEstatus;
}

void updateShiftRegister()
{
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, shiftbyte);
  digitalWrite(latchPin, HIGH);
}

void loop() {
  server.handleClient();
  // green to start with
  shiftbyte = 0b00000010;
  updateShiftRegister();

  if (!GATEstatus && openclick) {
    turnonsequence();
  }
  if (GATEstatus && !openclick) {
    turnonsequence();
  }
}

void handle_OnConnect() {
  GATEstatus = false;
  openclick = false;
  server.send(200, "text/html", SendHTML(false));
}

void handle_gateon() {
  openclick = true;
  server.send(200, "text/html", SendHTML(true));
}

void handle_gateoff() {
  openclick = false;
  server.send(200, "text/html", SendHTML(false));
}

void handle_NotFound() {
  server.send(404, "text/plain", "Not found");
}

String SendHTML(uint8_t gate) {
  String ptr = "<!DOCTYPE html>\n";
  ptr += "<html>\n";
  ptr += "<head>\n";
  ptr += "<title>Gate Control</title>\n";
  ptr += "</head>\n";
  ptr += "<body>\n";
  ptr += "<h1>Gate Control</h1>\n";
  ptr += "<p>Press to open and close gate.</p>\n";
  ptr += "<form method=\"get\">\n";
  if (gate)
    ptr += "<input type=\"button\" value=\"Gate open\" onclick=\"window.location.href='/gateoff'\">\n";
  else
    ptr += "<input type=\"button\" value=\"Gate closed\" onclick=\"window.location.href='/gateon'\">\n";
  ptr += "</form>\n";
  ptr += "</body>\n";
  ptr += "</html>\n";
  return ptr;
}

I really enjoyed this project and indeed I think that as part of my continuing learning experience I may repeat the whole project using I2C (two wires!) instead of a shift register in another blog post and video. Watch this space!

In the meantime I hope that you like the result.




Friday, February 10, 2023

0000 0000 1011 1100

The 3D flame in a vase

I was at an antique store recently, just browsing, not looking for anything in particular when I spied a lovely crystal vase - a steal at AUD$15.

Straight away I thought of the reflective and refractive possibilities should I shoehorn in a small solar panel and the stable joule thief (padauk version) together with the new "3D" LED PCB.

After soldering it all up and blasting the code onto the PFS154, I glued the lot into the vase and I'm happy to report that the light provided at night is quite lovely!

/*
  Candle with Three PWM

  Pseudo-random flickering to simulate a candle. Output is
  via 3xPWM channels, variables can be changed to
  alter the simulation

  Tue 17 Jan 2023 13:50:35 AEDT

*/

#include <stdint.h>
#include <stdlib.h>
#include "../device.h"
#include "../easy-pdk/calibrate.h"
#include "../auto_sysclock.h"
#include "../delay.h"
#include <stdbool.h>

#define LED4_BIT 4
#define LED0_BIT 0
#define LED3_BIT 3

uint16_t myrand = 2901;  // happy birthday

uint8_t slowcounter = 0;
uint8_t medcounter = 0;
uint8_t fastcounter = 0;
uint8_t slowstart = 0;
uint8_t slowend = 0;
uint8_t medstart = 0;
uint8_t medend = 0;
uint8_t faststart = 0;
uint8_t fastend = 0;
uint8_t faster = 0;

uint8_t waveslow[] = {20, 30, 70, 100};
uint8_t wavemed[] = {25, 40, 100, 120};
uint8_t wavefast[] = {20, 30, 120, 140};

bool fastup = true;
bool slowup = true;
bool medup = true;

void mydelay(uint8_t counter) {

  for (uint8_t thiscount = 0; thiscount <= counter; thiscount++) {
    _delay_us(1);
  }
}

uint16_t gimmerand(uint16_t small, uint16_t big) {
  myrand ^= (myrand << 13);
  myrand ^= (myrand >> 9);
  myrand ^= (myrand << 7);
  if (abs(myrand) % 13 == 0) {
    myrand = myrand - 23;
  }
  if (abs(myrand) % 17 == 0) {
    myrand = myrand + 11;
  }
  return abs(myrand) % 23 * (big - small) / 23 + small;
}

void getnewslow() {
  slowstart = gimmerand(waveslow[0], waveslow[1]);
  slowend = gimmerand(waveslow[2], waveslow[3]);
}

void getnewmed() {
  medstart = gimmerand(wavemed[0], wavemed[1]);
  medend = gimmerand(wavemed[2], wavemed[3]);
}

void getnewfast() {
  faststart = gimmerand(wavefast[0], wavefast[1]);
  fastend = gimmerand(wavefast[2], wavefast[3]);
  faster = gimmerand(2, 6);
}

// Main program
void main() {

  PAC |= (1 << LED4_BIT) | (1 << LED0_BIT) | (1 << LED3_BIT);

  // see datasheet
  PWMG1DTL = 0x00;
  PWMG1DTH = 0x00;
  PWMG1CUBL = 0xff;
  PWMG1CUBH = 0xff;
  PWMG1C = 0b10100111;
  PWMG1S = 0b00000000;

  PWMG0DTL = 0x00;
  PWMG0DTH = 0x00;
  PWMG0CUBL = 0xff;
  PWMG0CUBH = 0xff;
  PWMG0C = 0b10100111;
  PWMG0S = 0b00000000;

  PWMG2DTL = 0x00;
  PWMG2DTH = 0x00;
  PWMG2CUBL = 0xff;
  PWMG2CUBH = 0xff;
  PWMG2C = 0b10100111;
  PWMG2S = 0b00000000;

  getnewfast();
  getnewslow();
  getnewmed();
  slowcounter = slowstart;
  fastcounter = faststart;
  medcounter = medstart;

  // Main processing loop
  while (1) {

    // ramp up slow
    if (slowup) {
      slowcounter++;
      if (slowcounter > slowend) { // ramp finished so switch boolean
        slowup = !slowup;
      }
    }
    else {
      // ramp down slow
      slowcounter--;
      if (slowcounter < slowstart) { // ramp finished so switch boolean
        slowup = !slowup;
        getnewslow();
      }
    }

    // ramp up med
    if (medup) {
      medcounter++;
      if (medcounter > medend) { // ramp finished so switch boolean
        medup = !medup;
      }
    }
    else {
      // ramp down med
      medcounter--;
      if (medcounter < medstart) { // ramp finished so switch boolean
        medup = !medup;
        getnewmed();
      }
    }

    // ramp up fast
    if (fastup) {
      fastcounter = fastcounter + faster;
      if (fastcounter > fastend) { // ramp finished so switch boolean
        fastup = !fastup;
      }
    }
    else {
      // ramp down fast
      fastcounter = fastcounter - faster;
      if (fastcounter < faststart) { // ramp finished so switch boolean
        fastup = !fastup;
        getnewfast();
      }
    }
    // delay + a re-purposed random for ramp speeds
    mydelay(15 + faster);

    PWMG1DTL = slowcounter & 255;
    PWMG1DTH = slowcounter;
    PWMG0DTL = fastcounter & 255;
    PWMG0DTH = fastcounter;
    PWMG2DTL = medcounter & 255;
    PWMG2DTH = medcounter;

  }
}

// Startup code - Setup/calibrate system clock
unsigned char _sdcc_external_startup(void) {

  CLKMD = CLKMD_ILRC | CLKMD_ENABLE_ILRC | CLKMD_ENABLE_IHRC;
  CLKMD = CLKMD_ILRC | CLKMD_ENABLE_ILRC;
  PDK_SET_SYSCLOCK(SYSCLOCK_ILRC);
  EASY_PDK_CALIBRATE_ILRC(50000, 4000);

  return 0;
}

Judge the result for yourself - but this one has been chugging away at night time for a few weeks now and is a bit of a hit with the executive!



Thursday, February 2, 2023

0000 0000 1011 1011

Mailbag #28

A lovely bunch of items from China - the weird one was a sound activated USB LED module which for some reason I thought was broken when the video clearly shows it was dancing to my voice! It must have been late at night when I filmed that one.

Anyway, despite that gaffe, please enjoy - it's a LOT of LED action!