Saturday, December 19, 2020

0000 0000 0100 1110

Christmas supercomputer

Note: it is the summer holidays DownUnder, so this blog (and YT channel) will be taking a little break for a month - see you in 2021!

Who doesn't love red and green flashing LEDs at Christmas? For the last couple of years we have had these little CR2032 driven ATTiny13 chips flashing a couple of 5mm LEDs strewn throughout the house (and outside).

The microcontrollers are running at 1MHz and they are mostly sleep, just pumping out alternate 1/8th second blips to the LEDs followed by a 4 second gap. In this guise the coin cells last around 6-9 months (!!), although they do get a bit dim towards the end of that run.


This year I decided to update the project, again using ATTiny13a chips, but this time ambling along at 128kHz (less power) which switch 1206 SMD LEDs (less power at 1/4W), but flashing more frequently (1/8th second on, 1/2 second off). So it will be very interesting to see how they last through the festive season and beyond. I can't remember if the original was coded in assembly or not, but this one sure is:

;
; Device: ATtiny13A, Package: 8-pin-PDIP_SOIC
;
;            ______
;         1 /      |8
; RESET o--|       |--o VCC
;         2|       |7
;   PB3 o--|       |--o PB2
;         3|       |6
;   PB4 o--|       |--o PB1
;         4|       |5
;   GND o--|       |--o PB0
;          |_______|
;

.nolist
.include "tn13adef.inc"         ; Define device ATtiny13A
.list

; **********************************
;       R E G I S T E R S
; **********************************

.def rSreg = R15                   ; Save/Restore status port
.def rmp = R16                     ; Define multipurpose register
.def colour = R1

; **********************************
;           S R A M
; **********************************

.dseg
.org SRAM_START

; **********************************
;         C O D E
; **********************************

.cseg
.org 000000

; **********************************
; R E S E T  &  I N T - V E C T O R S
; **********************************
    rjmp Main                        ; Reset vector
    reti ; INT0
    reti ; PCI0
    reti ; OVF0
    reti ; ERDY
    reti ; ACI
    reti ; OC0A
    reti ; OC0B
    rjmp mytimer_ISR                 ; watchdog timer interrupt
    reti ; ADCC

; **********************************
;  I N T - S E R V I C E   R O U T .
; **********************************

; Add all interrupt service routines

mytimer_ISR:

     ; as I understand it, cli and sei are automatic on
     ; entry and exit respectively of ISR

     in r16, SREG      ; save SREG
     push r16          ; do this if you want to use

finishisr:

     pop r16           ; do this if you pushed SREG above
     out SREG, r16     ; restore SREG

     reti

; **********************************
;  M A I N   P R O G R A M   I N I T
; **********************************

Main:
    ldi rmp,Low(RAMEND)
    out SPL,rmp                    ; Init LSB stack pointer

    ldi r16, 0b00011000            ; output mode for PB3/4
    out DDRB, r16
    ldi r16, 0b00010000
    mov colour, r16                 ; red is on

    ; turn on PB4, enable internal pullups
    ; on the rest of the pins except PB3
    ldi r16, 0b11110111
    out PORTB, r16

; **********************************
;    P R O G R A M   L O O P
; **********************************

Loop:

    rcall waitabit                 ; 1/16 second subroutine
    rcall waitalot                 ; 1/2 second subroutine

    rjmp Loop

waitabit:

    wdr                      ; reset watchdog timer
    ldi r16, 0b01000010      ; 1/8 second timer
    out WDTCR, r16
    rcall sleepnow           ; sleep subroutine
    rcall flipbit            ; flip the led, if off then on, etc.
    ret

waitalot:

    wdr                      ; reset watchdog timer
    ldi r16, 0b01000101      ; 1/2 second timer
    out WDTCR, r16
    rcall sleepnow           ; sleep subroutine
    rcall flipbit            ; flip the leds
    ret

sleepnow:

    ; sleep enable, power down mode
    ldi r16, 0b00110000
    out MCUCR, r16

    ; disable ADC
    ldi r16, 0b00010000
    out ADCSRA, r16

    ; disable interrupts to make sure the following
    ; timed sequence is not interrupted
    cli

    ; disable BOD - must be done in a timed sequence
    ; as follows, write 1 to BODS and BODSE then within
    ; four clock cycles write 1 to BODS and 0 to BODSE
    ; then within three clock cycles enable sleep
    ; page 33 on the datasheet

    ldi r16, 0b00000011
    ldi r17, 0b00000010
    out BODCR, r16
    out BODCR, r17

    sei
    sleep

    ; sleep disable
    ldi r16, 0b00000000
    out MCUCR, r16

    ret

flipbit:

    in r16, pinb              ; what is the state of the pins?
    andi r16, 0b00011000      ; load red or green on
    brbc 1, turnoff

testred:
    mov r16, colour
    ldi r17, 0b00001000
    cp r16, r17
    brbs 1, greenon

redon:
    ldi r16, 0b00001000       ; set colour red
    mov colour, r16           ; and make it so
    ldi r16, 0b00001000
    out portb, r16
    rjmp finishflip

greenon:
    ldi r16, 0b00010000       ; set colour green
    mov colour, r16           ; and make it so
    ldi r16, 0b00010000
    out portb, r16
    rjmp finishflip

turnoff:
    ldi r16, 0b00000000
    out portb, r16

finishflip:

    ret

; End of source code


The only downside is that with the frantic flashing the place looks like an alien landing site or a Christmas super computer - good one!

In line with some other current projects, it is my solemn promise to make next year's batch from Pesky Padauk microcontrollers, probably the PMS150C.




Saturday, December 12, 2020

0000 0000 0100 1101

Orange Pi temperature control

There is an Orange Pi Zero attached to my 3D printer running OctoPrint as per a previous post and YouTube video. On the assumption that it might run hot at times (introducing the possibility of a throttle or shutdown scenario) I connected a fan to the top of the printed enclosure which runs continuously off the 5V rail.


The fan is a bit noisy, although that doesn't matter as the unit is located downstairs in the garage/workshop, but also I was thinking that it may not be necessary to have the fan running all of the time. Maybe it only needs to cut in and out as the temperature of the board changes?

While the Orange Pi Zero is working well with the printer at the moment I don't want to hassle it too much, so to test a few ideas kicking around in my noggin I grabbed an Orange Pi PC as a guinea pig instead.

I loaded an SD card with the appropriate version of Armbian (choosing the "focal" version). Included with the OS is the command "armbianmonitor -m" which can check the temperature of the SOC directly. You can also find out the temperature via the command:

"cat /sys/class/thermal/thermal_zone0/temp"

...which will output something like 29200 if the temperature is 29.2°C.

The first (and most recommended) method of temperature mitigation for this board is to attach a heat sink to the SOC, in this case an ARM H3+.


Without doing extensive tests, this one simple modification did lower the temperature quite a bit - and I'm pretty sure from reading a few of the forums posts out there that this would be sufficient to keep the board cool. The only thing is that if the board does start to overheat, then performance is throttled and perhaps a a long print run might end up being interrupted and/or stopped - very sad!

Therefore, I 3D-printed a suitable enclosure that allowed for a fan to be installed directly over the SOC.

The fan works directly from either the 5V or the 3.3V rail as per the Orange Pi Zero version, but here I wanted to dive a little deeper and control the fan according to need. After reading about many options, I found this site which recommended a GPIO linked to a transistor as a switching mechanism (I've altered the circuit to suit as shown below).

Then, by controlling the GPIO status (HIGH or LOW), it should be possible to turn on and off the fan as required. Although many sites recommended a python-based solution for the control of the pin, I chose a bash script instead as per this github repository because I plan to have the script run at start-up (perhaps as a daemon) and this solution seemed the most likely to succeed.

I had to modify the script a bit (to include for instance hysteresis and address some scripting errors), but it ended up being as follows:

#!/bin/sh -e
#
#fantemp.sh
#
# see https://github.com/orangepi-xunlong/wiringOP for building wiringOP
#
# +------+-----+--------+OrangePiH3+-------+-----+------+
# | GPIO | wPi | Name	| Physical | Name  | wPi | GPIO |
# +------+-----+--------+----++----+-------+-----+------+
# |      |     |   3.3V |  1 || 2  | 5V	   | 	 |      |
# |   12 |   0 |  SDA.0 |  3 || 4  | 5V	   | 	 | 	|
# |   11 |   1 |  SCL.0 |  5 || 6  | GND   | 	 | 	|
# |    6 |   2 |    PA6 |  7 || 8  | TXD.3 | 3   | 13   |
# |      |     |    GND |  9 || 10 | RXD.3 | 4   | 14   |
# |    1 |   5 |  RXD.2 | 11 || 12 | PD14  | 6   | 110  |
# |    0 |   7 |  TXD.2 | 13 || 14 | GND   | 	 | 	|
# |    3 |   8 |  CTS.2 | 15 || 16 | PC04  | 9   | 68   |
# |      |     |   3.3V | 17 || 18 | PC07  | 10  | 71   |
# |   64 |  11 | MOSI.0 | 19 || 20 | GND   | 	 | 	|
# |   65 |  12 | MISO.0 | 21 || 22 | RTS.2 | 13  | 2    |
# |   66 |  14 | SCLK.0 | 23 || 24 | CE.0  | 15  | 67   |
# |      |     |    GND | 25 || 26 | PA21  | 16  | 21   |
# |   19 |  17 |  SDA.1 | 27 || 28 | SCL.1 | 18  | 18   |
# |    7 |  19 |   PA07 | 29 || 30 | GND   | 	 | 	|
# |    8 |  20 |   PA08 | 31 || 32 | RTS.1 | 21  | 200  |
# |    9 |  22 |   PA09 | 33 || 34 | GND   | 	 | 	|
# |   10 |  23 |   PA10 | 35 || 36 | CTS.1 | 24  | 201  |
# |   20 |  25 |   PA20 | 37 || 38 | TXD.1 | 26  | 198  |
# |      |     |    GND | 39 || 40 | RXD.1 | 27  | 199  |
# +------+-----+--------+----++----+-------+-----+------+
# | GPIO | wPi | Name	| Physical | Name  | wPi | GPIO |
# +------+-----+--------+OrangePiH3+-------+-----+------+
#

# see above table
PIN=6

# two temperatures for hysteresis
TEMP1=35000 # 35 degrees
TEMP2=45000 # 45 degrees

gpio mode $PIN out

while true; do
  if [ $(cat /sys/class/thermal/thermal_zone0/temp) -gt $TEMP1 ] \
  && [ $(cat /sys/class/thermal/thermal_zone0/temp) -gt $TEMP2 ]
  then
    gpio write $PIN 1
  elif [ $(cat /sys/class/thermal/thermal_zone0/temp) -lt $TEMP2 ] \
    && [ $(cat /sys/class/thermal/thermal_zone0/temp) -lt $TEMP1 ]
  then
    gpio write $PIN 0
  fi
  sleep 5
done

As part of testing, I dialed up a stress test as follows:

stress --cpu 8 --io 4 --vm 2 --vm-bytes 128M --timeout 45s

Monitoring of the CPU showed the temperature shooting up, and then the fan coming on and keeping everything cool - only to turn off when the "stress" is off. Very good result!

The final part was to solder up a board with the extra bits including the transistor to be placed inside the cooled housing, and make all the connections as per the breadboard test.

Now all I need to do is to retrofit this solution to the Orange Pi Zero attached to the printer and that will be mission accomplished!




Saturday, December 5, 2020

0000 0000 0100 1100

What is better than one QX5252 chip?

I was musing about two recent but separate projects, one which sings the praises of the QX5252 chip and the other which throws the ATTiny13 chip into a watchdog role. What if the watchdog could effectively have endless energy via the big yellow in the sky, and so can run "forever" and be available to do watchdog-type activities?

I decided to test the addition of a second QX5252, one chip operating to "pump" out stable energy to the microcontroller, and the other to grab ergs from the sun when available.

Normally with the QX5252 and similar ICs the chip shuts down the output of the circuit if there is enough light, and all of the solar panel energy is shunted to the rechargeable battery for the next night of LED fun. If, however, you want the battery to simultaneously charge and discharge during the day (is that even possible?) then one variation worth exploring is to maybe employ a second QX5252 just for that purpose.

So the plan is:

  1. use the "normal" stable joule thief PCB and program an ATTiny13 to do something (probably blink), but do not connect a Solar Panel to the board so that the QX5252 is fooled into being always in "discharge" mode
  2. add another QX5252 to the rail, but have no output components such that it is charging the battery if there is any light available to do so
In part (1), above, I struggled to write the assembly code which initially seemed an easy variation on previous projects. All I wanted to do was use two LEDs and flash them alternatively for 2 seconds on then 8 seconds off - e.g. red on (2 seconds), all off (8 seconds), yellow on (2 seconds), all off (8 seconds), rinse and repeat...

Of course, if the states of the LEDs are not changing then the microcontroller can sleep to save power. Also the little fella should be running anaemically at 128kHz thus drawing minimum current.

Eventually the code worked, but if you can see a better way to more efficiently and/or clearly write these routines, please leave a comment on the YouTube channel.

.nolist
.include "tn13adef.inc" ; Define device ATtiny13A
.list
; **********************************
;        H A R D W A R E
; **********************************

; Device: ATtiny13A, Package: 8 - pin - PDIP_SOIC
;
;           ____________
;        1 /            | 8
;      o-- | RESET  VCC | --o
;      o-- | PB3    PB2 | --o
;      o-- | PB4    PB1 | --o
;      o-- | GND    PB0 | --o
;        4 |____________| 5
;

; **********************************
;         C O D E
; **********************************

    .cseg
    .org 000000

; **********************************
; R E S E T  &  I N T - V E C T O R S
; **********************************
    rjmp Main               ; Reset vector
    reti ; INT0
    reti ; PCI0
    reti ; OVF0
    reti ; ERDY
    reti ; ACI
    reti ; OC0A
    reti ; OC0B
    rjmp mytimer_ISR        ; my interrupt
    reti ; ADCC

; **********************************
;  I N T - S E R V I C E   R O U T .
; **********************************

mytimer_ISR:

    ;  sleep is interrupted, program returns
    reti

; **********************************
;  M A I N   P R O G R A M   I N I T
; **********************************
;
Main:

    ldi r16, Low(RAMEND)
    out SPL, r16              ; Init LSB stack pointer

    ; output mode for PB3, PB4, input for the rest
    ldi r16, 0b00011000
    out DDRB, r16

    ; turn on PB4, enable internal pullups
    ; on the rest unused pins
    ldi r16, 0b11110111
    out PORTB, r16

    ; which LED is active?
    ldi r18, 0b00010000             ; start with PB4

; **********************************
;    P R O G R A M   L O O P
; **********************************

Loop:

    rcall waitabit            ; 2 second (on)
    rcall waitalot            ; 8 seconds (off)
    rjmp loop

waitabit:

    ; reset watchdog timer
    wdr

    ; 2 second timer
    ldi r16, 0b01000111       ; see datasheet
    out WDTCR, r16

    rcall sleepnow
    rcall flipbit
    ret

waitalot:

    ; reset watchdog timer
    wdr

    ; 8 second timer
    ldi r16, 0b01100001      ; see datasheet
    out WDTCR, r16

    rcall sleepnow
    rcall flipbit
    ret


sleepnow:

    ; see datasheet MCUCR, ADCSRA, BODCR

    ; sleep enable, power down mode
    ldi r16, 0b00110000
    out MCUCR, r16

    ; disable ADC
    ldi r16, 0b00010000
    out ADCSRA, r16

    ; disable interrupts to make sure the following
    ; timed sequence is not interrupted
    cli

    ; disable BOD - must be done in a timed sequence
    ; as follows, write 1 to BODS and BODSE then within
    ; four clock cycles write 1 to BODS and 0 to BODSE
    ; then within three clock cycles enable sleep
    ; page 33 on the datasheet

    ldi r16, 0b00000011
    ldi r17, 0b00000010
    out BODCR, r16
    out BODCR, r17

    sei

    sleep

    ; sleep disable
    ldi r16, 0b00000000
    out MCUCR, r16

    ret

flipbit:

    cpi r18, 0b00010000    ; is pb4 active?
    brbs 1, workonpb4

workonpb3:

    in r16, portb
    andi r16, 0b00001000   ; mask all but PB3
    cpi r16, 0b00001000    ; is pb3 on?

    brbs 1, turnoffpb3

    in r16, portb          ; flip PB3
    ldi r17, 0b00001000
    eor r16, r17
    out portb, r16

    rjmp finishflip

turnoffpb3:

    in r16, portb          ; flip PB3
    ldi r17, 0b00001000
    eor r16, r17
    out portb, r16
    ldi r18, 0b00010000    ; make PB4 active

    rjmp finishflip

workonpb4:

    in r16, portb
    andi r16, 0b00010000
    cpi r16, 0b00010000    ; is PB4 on?

    brbs 1, turnoffpb4

    in r16, portb          ; flip PB4
    ldi r17, 0b00010000
    eor r16, r17
    out portb, r16

    rjmp finishflip

turnoffpb4:

    in r16, portb          ; flip PB4
    ldi r17, 0b00010000
    eor r16, r17
    out portb, r16
    ldi r18, 0b00001000    ; make PB3 active

finishflip:

ret

; End of source code


I was very stoked to see this circuit and code not only work as intended, but also hold its own over weeks of testing, oscillating between about 1.33V after a day of sunshine to 1.25V after a couple of days and nights of overcast misery. Nice one two QX5252 Frankenstein bloke!

The only question remaining is: should I add a couple of diodes going to the rail from each of the outputs so they don't interfere with each other? Are the losses so great (although they would probably be Schottky diodes for minimum forward voltage) that it's not worth the addition? There are more tests to be made, but for now enjoy the <usual> blinking madness. 







Saturday, November 28, 2020

0000 0000 0100 1011

Not really electronics (or is it?)

A quick tour of the 3D printing bench this week, mainly for me so that I don't forget all of the twists and turns in making the tracking software/hardware and the photographing software/hardware work as expected and required.

1. The printer

I had been thinking about a 3D printer for a couple of years, for a couple of reasons. Firstly, I hate jumping in the car and driving 30 minutes to the nearest hardware store to grab some stupid piece of plastic that costs $7 to buy, but probably one hundredth of that price to manufacture. A waste of time and money!

Secondly, there have been some projects which I have shoe-horned into off the shelf boxes, but then I have had to modify/extend etc., because some cable or other wasn't quite fitting in the enclosure.

Finally, I saw PileOfStuff and his Creality Ender 3 V2 and the die was cast. My limited research had also pointed me towards this model, and so after negotiations with the Minister of Finance I was able to purchase the unit locally and then wait by the postbox for it's arrival. While I waited (a week or so) I consumed all manner of YouTube videos about how to unpack, setup and test the printer, before settling on this explanation which worked fine. The whole process from box to first print took around 2 hours which I thought was pretty reasonable.

One extra useful step was to download and have handy some gcode that assists with leveling the print bed. I did it manually a couple of times (thanks YouTube), but the code makes it so much easier, and semi-automatic.

The Minister of Finance demanded that the first print be all about plastic pigs, and so this was the test run result while I fiddled with all the settings in the learning process (and in the end only one pig trotter fell off - to be superglued back on with nobody the wiser).


A week or so and after a few prints I came to realise that the bed was having trouble maintaining consistent temperature in the basement workshop, mainly because it could be as low as 3 degrees Celsius down there even at this time of year, with the odd breeze when doors are opened.

Also the space is a bit dusty which is not great for electronics or servo motors. I had been covering up the printer with a big plastic rubbish bag when not in use, but in the end I placed an additional online order for a medium Creality enclosure, and although it turned out to be comically large it was of course suitable for the task.


2. The OctoPrint solution.

On cold nights after work I was becoming more reluctant to trudge down and check the printer, so I downloaded Armbian for an Orange Pi Zero that I had lurking about in a cupboard. 

Quite a few online gurus were adamant that this was a bad combination, and that I would need to buy a late model Raspberry Pi to have any success. But after following an excellent online tutorial I had the thing up and running and reporting back the progress (or otherwise) of the print.

Occasionally a print was "lost" and the printer would grind to a halt awaiting further instructions. It turns out that the mountain of concrete between the upstairs router and the puny antenna of the Orange Pi Zero could be the weakest link.

In response to this issue I pressed into service a cheapo WiFi extender laying about doing very little, and all has been fine since.


The first thing I printed from this new setup was a nice box for the Orange Pi Zero, and I slapped a fan on the top with 5V power provided by the main board. It works a treat in keeping the board cool and calm.


I was also motivated to 3D print a side holder for the filament for a couple of reasons. Firstly, the filament wasn't flowing real well and it sometimes became "semi-stuck" with the wacky angle of attack from the top of the printer.

Secondly, I eventually would like to put the printer up on a concrete block with vibration dampers as seen in this video - and so to fit in the "medium" enclosure the spool must migrate to the side, providing the needed vertical space.

The side spool holder print went well and the result is lovely (and works a treat!). I did add a homemade spring to help align the spool with the feeder.


3. Getting a picture

I started to think about how I could get an actual picture of the print so that I could monitor the printer from the warmth and comfort of an armchair upstairs. Experts online assured me that the Orange Pi Zero would struggle, although I still like the idea of using an old laptop camera wired in for the job.

However, I did also have an ESP-32 Cam module acquired for a song which maybe could be pressed into service. The example camera webserver code provided with the ESP-32 board definitions was simple and worked well, but I could not see the ongoing printing in the low light of the enclosure, and as well it seemed silly that the camera module (and printed box) had a bright LED but that it didn't seemed to be accessible on the standard web server page.

After a quick play with a bluetooth option (it slowed down picture acquisition too much for me) along comes the genius of Hermann Stamm-Wilbrandt and his modified webserver...and we now have a "flash" button to play with in order to light up the scene and take great pics or streams of the printing action. Nice one, Hermann!

I then printed a 3D printed box to wrap the ESP32 in (with two warnings: the lid needs to be separated out from the other components - I used "MeshLab" and then it needed to be flipped in Ultimaker Cura for access to the 5V/GND pins; also make sure that you do at least a 40% infill for added strength needed for the "ball" part of the construction) and all went well. You can see mine printed below with a modified white lid indicating the flipped access point.



#include "esp_camera.h"
#include <WiFi.h> // // WARNING!!! Make sure that you have either selected ESP32 Wrover Module, // or another board which has PSRAM enabled // // Select camera model //#define CAMERA_MODEL_WROVER_KIT //#define CAMERA_MODEL_ESP_EYE //#define CAMERA_MODEL_M5STACK_PSRAM //#define CAMERA_MODEL_M5STACK_WIDE #define CAMERA_MODEL_AI_THINKER #include "camera_pins.h" const char* ssid = "*********"; const char* password = "*********"; const int pin_vsync = VSYNC_GPIO_NUM; void startCameraServer(); void setup() { Serial.begin(115200); Serial.setDebugOutput(true); Serial.println(); camera_config_t config; config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; config.pin_d0 = Y2_GPIO_NUM; config.pin_d1 = Y3_GPIO_NUM; config.pin_d2 = Y4_GPIO_NUM; config.pin_d3 = Y5_GPIO_NUM; config.pin_d4 = Y6_GPIO_NUM; config.pin_d5 = Y7_GPIO_NUM; config.pin_d6 = Y8_GPIO_NUM; config.pin_d7 = Y9_GPIO_NUM; config.pin_xclk = XCLK_GPIO_NUM; config.pin_pclk = PCLK_GPIO_NUM; config.pin_vsync = VSYNC_GPIO_NUM; config.pin_href = HREF_GPIO_NUM; config.pin_sscb_sda = SIOD_GPIO_NUM; config.pin_sscb_scl = SIOC_GPIO_NUM; config.pin_pwdn = PWDN_GPIO_NUM; config.pin_reset = RESET_GPIO_NUM; config.xclk_freq_hz = 20000000; config.pixel_format = PIXFORMAT_JPEG; //init with high specs to pre-allocate larger buffers if(psramFound()){ config.frame_size = FRAMESIZE_UXGA; config.jpeg_quality = 10; config.fb_count = 2; } else { config.frame_size = FRAMESIZE_SVGA; config.jpeg_quality = 12; config.fb_count = 1; } #if defined(CAMERA_MODEL_ESP_EYE) pinMode(13, INPUT_PULLUP); pinMode(14, INPUT_PULLUP); #endif // camera init esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed with error 0x%x", err); return; } sensor_t * s = esp_camera_sensor_get(); //initial sensors are flipped vertically and colors are a bit saturated if (s->id.PID == OV3660_PID) { s->set_vflip(s, 1);//flip it back s->set_brightness(s, 1);//up the blightness just a bit s->set_saturation(s, -2);//lower the saturation } //drop down frame size for higher initial frame rate s->set_framesize(s, FRAMESIZE_QVGA); #if defined(CAMERA_MODEL_M5STACK_WIDE) s->set_vflip(s, 1); s->set_hmirror(s, 1); #endif WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); startCameraServer(); Serial.print("Camera Ready! Use 'http://"); Serial.print(WiFi.localIP()); Serial.println("' to connect"); } void loop() { // put your main code here, to run repeatedly: delay(10000); }


No doubt there are many more mods for this printer to come (e.g. a filament guide) but at this point and after a couple of weeks I have to say that it's a very good printer for the price, and the super cheap additions of an Orange Pi Zero and ESP-32 CAM just make the whole printing process so much easier to set up and monitor.




Saturday, November 21, 2020

0000 0000 0100 1010

PCF8563 Time/Date IC

Slightly obsessed with timing devices (really?!), I saw these PCF8563 real time clock chips that had so many features and looked too good to be true (and so cheap!). I grabbed some that were already made up in modules, and others that are just raw IC chips ready to be used in an appropriate circuit.

In this blog I am going to focus on the raw IC version and try to get it going so that I can measure and store time.

The first order of business is to solder up the SOP8 package onto a DIP8 adapter. I put some extra white paint on the little fella so that I know it's not an AVR or PADAUK when I reach for it!


The plan is to put this on a breadboard and then:

  1. program an Arduino Nano to push out the time/date to the RTC
  2. everything sleeps in low current mode
  3. pushing a button wakes up the Nano
  4. the Nano polls the RTC for time and date (and turns on an LED for good measure, also who doesn't like a blinking LED?)
  5. the Nano pushes out the time/date to an OLED display for a few seconds
  6. everyone goes back to sleep
  7. battery backup the whole shebang with a coin cell battery?
  8. swap out the Nano for an ATMega328 to further reduce the current requirements?

I am more than a little unhappy with the internet over this little circuit, for a couple of reasons. Firstly when I looked for a likely setup, I came across this site with the following picture.


Even though from previous experience with crystals I knew that capacitors need to be used to get the things oscillating, the only capacitor mentioned on the page is for decoupling and it's not near the crystal.

Upshot? I spent a "happy" couple of hours trying to debug code that clearly showed the Nano talking to the RTC, and vice versa, but the RTC not "ticking" in any clock-like manner. This problem was eventually solved with the addition of a 20pf capacitor between the crystal and ground. Do I need to put one on the other side? I'm not sure! At this point I was just happy to see the little fella measuring the <frustrating long> passage of time.


Secondly there were a LOT of libraries purporting to help communicate with the PCF8563. Sadly many had holes and it took quite a bit of fiddling to realise that this library is the easiest and most functional. It's found and installed very quickly using the Arduino IDE "Library Manager" dialog.

I also realised that for my sanity to prevail I would work with two lots of (very similar) code, one to set the RTC and one to then read it on demand. I have seen these combined on some sites with similar software via a "flag" or test, but it seemed just as easy to separate out these similar but fundamentally different functions, even though much of the rest of the code is the same.

I've highlighted the essential difference.

Setting Code:

/*
   Using an PCF8563 as an RTC for an Arduino Nano
   and an OLED display

   One Circuit Friday 13 November 22:26:09 AEDT 2020
   Friday 13th! Yikes!
*/

#include <Wire.h>
#include "RTClib.h"
#include <avr/sleep.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// some variables and constants
byte thehour = 0;
byte theminute = 0;
byte thesecond = 0;
byte theday = 0;
byte themonth = 0;
int theyear = 0;
String myminute = "minute";
String mysecond = "second";
const byte ledPin = 4;
const byte interruptPin = 2;
volatile boolean button = false;

#define OLED_RESET -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32

// initialise our I2C devices
RTC_PCF8563 rtc;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// button ISR
void wakeUpNow() {
  button = true;
}

// sleep routine
void sleepNow() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  
  sleep_enable();         
  sleep_mode(); 
  sleep_disable(); 
  detachInterrupt(0);
}

void setup () {
  while (! rtc.begin()) {
    delay(500);
  }
  delay(1000); // settle rtc
  pinMode(ledPin, OUTPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), wakeUpNow, RISING);
  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64
    for (;;);
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10, 0);
  display.println("OneCircuit Clock");
  display.display();
  delay(3000);
  display.clearDisplay();
  display.display();
}

void showtime() {
  digitalWrite(ledPin, HIGH);
  DateTime now = rtc.now();
  thehour = now.hour();
  theminute = now.minute();
  // make it pretty if the numbers are below 10
  if (theminute < 10) {
    myminute = "0" + String(theminute);
  }
  else {
    myminute = String(theminute);
  }
  thesecond = now.second();
  if (thesecond < 10) {
    mysecond = "0" + String(thesecond);
  }
  else {
    mysecond = String(thesecond);
  }
  theday = now.day();
  themonth = now.month();
  theyear = now.year();
  // string together the date/time (pun intended)
  String nowtime = "TIME " + String(thehour) + ":" + myminute + ":" + mysecond;
  String nowdate = "DATE " + String(theday) + "/" + String(themonth) + "/" + String(theyear);
  display.setCursor(10, 0);
  display.println("OneCircuit Clock");
  display.setCursor(10, 15);
  display.println(nowtime);
  display.setCursor(10, 25);
  display.println(nowdate);
  display.display();
  delay(5000);
  digitalWrite(ledPin, LOW);
  display.clearDisplay();
  display.display();
  button = false;
  attachInterrupt(digitalPinToInterrupt(interruptPin), wakeUpNow, RISING);
}

void loop() {
  if (button) {
    showtime();
  }
  sleepNow();
}

Reading Code:

/*
   Using an PCF8563 as an RTC for an Arduino Nano
   and an OLED display

   One Circuit Friday 13 November 22:26:09 AEDT 2020
   Uh Oh - Friday 13th...
*/

#include "RTClib.h"
#include <avr/sleep.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// some variables and constants
byte thehour = 0;
byte theminute = 0;
byte thesecond = 0;
byte theday = 0;
byte themonth = 0;
int theyear = 0;
String myminute = "minute";
String mysecond = "second";
const byte ledPin = 4;
const byte interruptPin = 2;
volatile boolean button = false;
#define OLED_RESET -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32

// initialise our I2C devices
RTC_PCF8563 rtc;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// button ISR
void wakeUpNow() {
  button = true;
}

// sleep routine
void sleepNow() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  sleep_mode();
  sleep_disable();
  detachInterrupt(0);
}

void setup () {
  pinMode(ledPin, OUTPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  while (! rtc.begin()) {
    delay(500);
  }
  delay(1000); // settle rtc
  attachInterrupt(digitalPinToInterrupt(interruptPin), wakeUpNow, RISING);
  DateTime now = rtc.now();
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    for (;;);
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10, 0);
  display.println("OneCircuit Clock");
  display.display();
  delay(3000);
  display.clearDisplay();
  display.display();
}

void showtime() {

  digitalWrite(ledPin, HIGH);
  DateTime now = rtc.now();
  thehour = now.hour();
  theminute = now.minute();
  // make it pretty if the numbers are below 10
  if (theminute < 10) {
    myminute = "0" + String(theminute);
  }
  else {
    myminute = String(theminute);
  }
  thesecond = now.second();
  if (thesecond < 10) {
    mysecond = "0" + String(thesecond);
  }
  else {
    mysecond = String(thesecond);
  }
  theday = now.day();
  themonth = now.month();
  theyear = now.year();
  // string together the date/time (pun intended)
  String nowtime = "TIME " + String(thehour) + ":" + myminute + ":" + mysecond;
  String nowdate = "DATE " + String(theday) + "/" + String(themonth) + "/" + String(theyear);
  display.setCursor(10, 0);
  display.println("OneCircuit Clock");
  display.setCursor(10, 15);
  display.println(nowtime);
  display.setCursor(10, 25);
  display.println(nowdate);
  display.display();
  delay(5000);
  digitalWrite(ledPin, LOW);
  display.clearDisplay();
  display.display();
  button = false;
  attachInterrupt(digitalPinToInterrupt(interruptPin), wakeUpNow, RISING);
}

void loop() {
  if (button) {
    showtime();
  }
  sleepNow();
}

After all of this fiddling about I was able to breadboard the devices together and make the clock a reality.


The battery backup proved useful for kicking out the programming USB cable as the power supply and running the device independently via a battery bank, with a coin cell battery stuck in to keep the clock ticking.

For backup power to work properly I needed to put in a diode (1N4148 in my case) from the 5V to the IC and another one from the battery (3.3V) to the IC. I'm pretty sure a Schottky would be fine as well for this job. The diodes prevent the backup from trying to run the whole circuit when the power is out, and also to prevent the 5V from the main battery/power source from interfering with the coin cell.

Just for "fun" and as seen in the picture above, I also compiled the code for the ATMega328 which worked fine - nice one!