Posts

Showing posts from 2018

Replacing the camera on the MJX Bugs B2W

Image
The MJX Bugs B2W drone is great to fly. It stays relatively stable even in moderate wind, has great range, and the return-to-home function coupled with GPS means you are unlikely to lose your drone even when it goes out of range.

The onboard WIFI camera, however, is another story. I could never get it to go beyond 100m (that's paired with an iPad Air 2, which gives better range than all the smartphones I have tried). Even when it's within range, the video is both laggy and jerky.

So there's no other choice but to replace the camera with a proper 5.8GHz FPV. These were the parts I bought:

Foxeer Arrow Micro Pro 2.1mm cameraEachine TX526 5.8G VTXEachine ROTG02 FPV OTG receiver
Many people have tried different ways of modding the B2W. Some chose to preserve the onboard camera while adding the new one (I decided to rip the whole thing out to reduce the weight). Some chose to power the camera + VTX with its own battery (I chose to power the assembly with the onboard camera batte…

Google Camera with NightSight on Redmi Note 4x

Image
I have been running Google Camera 5.x on a rooted Redmi Note 4x / MIUI9 for the longest time. The default HDR+ shots are fantastic, and the portrait mode is fun and surprisingly reliable.

I generally dislike flashing new ROMs due to the hassle of configuring the environment and having to setup the odd apps that do not properly restore their settings from the cloud. So I have stuck with MIUI and have generally grown quite used to it.

A number of factors have finally nudged me out of my comfort zone. First, the MIUI10 update was a total disaster. Google Camera stopped working with no known workaround. Once one has tasted the power of Google Camera, it's hard to go back to anything else! Secondly, MIUI10 is an aesthetic mess. It abandoned color notification icons for the black-and-white aesthetic of vanilla Android, but left its own apps (eg. Mi Fit) alone. This led to something so horrible that even a typically function-over-form guy like myself couldn't stand it. Thirdly, this …

Current draw of ATtiny85 ticking clock using watchdog timer

I modified the previous watchdog timer code to test the current draw of ATtiny85 ticking a clock using POWER_DOWN deep sleep.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76#include <avr/io.h>#include <avr/wdt.h>#include <avr/sleep.h>#include <avr/interrupt.h>#include <avr/power.h>#define TIMER0_PRESCALER 1024#define OCR0A_DEFVAL ((byte)(F_CPU / (float)TIMER0_PRESCALER * 200/1000.0) - 1)#define adc_disable() (ADCSRA &= ~(1<<ADEN)) // disable ADC (before power-off)volatile byte timer0_tickpin = PB3; volatilebool wdtimer =false; voidstartTimer0() { // Set prescaler to 1024, thereby starting Timer0 TCCR0B = bit(CS02) | bit(CS00); } voidstopTimer0() { // Set prescaler to 0, thereby stopping Timer0 TCCR0B =0; } // Interrupt service routine for Timer0// The effect…

ESPCLOCK2, Part 4 - Implementation

Image
Parts required:WeMos D1 MiniATtiny850.47F capacitorBAT46 Schottky diodePushbutton 2 x 100 ohm resistors2 x 4.7K ohm resistors Breadboard layout:
Source codeGitHub Operation When the circuit is first powered up, the onboard LED on the D1 Mini will light up, indicating that it is in configuration mode. 
Selecting the captive wifi portal "ESPCLOCK2" will bring up the web browser. The configuration page looks like this: Assuming that the timezone has been prefilled correctly, one only has to select the local wifi AP and supply the password, then key in the time on the physical clock in HHMMSS format. Hit the [Save] button, the onboard LED should turn off, and the clock will start to run. Hardware When the pushbutton is held down while the circuit is powered up, the D1 mini clears out the wifi AP and password, allowing you to start over again from a clean slate.
The Schottky diode cuts off the link to the 3.3V pin when the battery is depleted or removed. The ATtiny85 will then b…

Current draw of a D1 Mini in deep sleep

To measure the deep sleep current draw of the D1 Mini, I hooked up 4 NiMH AA batteries in series (~5.2V) to its 5V and GND pins.

The sketch uploaded to the D1 Mini was a nominal:

void setup() { ESP.deepSleep(60)*60*1000000UL, WAKE_RF_DEFAULT); } void loop() { }
The current draw was a pretty steady 0.8mA, or 800uA. That's a obviously a far cry from the sub-100uA reportedly achievable with the barebones ESP-01 due to all the extra components on the D1 Mini.

Reported deep sleep current draw for the D1 Mini is all over the place, from 0.21mA (5V), to 0.3mA (3.3V), to 6mA (USB)!
Notes: 1. Connecting a 18650 battery (~4V) to the 5V pin did not work. In theory, the MC6211 LDO used by the D1 Mini means anything higher than 3.56V should work. But when connected, the onboard LED started to flash in a slow but erratic fashion, I suspect it is randomly resetting (because each time the D1 Mini powers up, the onboard LED flashes briefly).

2. Connecting a 18650 battery to the 3.3V pin did wor…

Coulomb, mA and mAh

I am sure I will forget all this later, so...

A Coulomb (C) is defined as 1A * 1sec. This implies 1A = 1C/sec.

In the previous example, we have a 4V/2600mAh battery driving a 1K resistor. Through the LTC4150, we are measuring consumption of 0.614439C in 150.8s,

    Load = 0.614439C / 150.8s = 0.004075C/s = 4.075mA
    Therefore battery should last 2600mAh/4.075mA = 638 hours

What if you have varying loads? eg.

    (1) Load = 0.614439C for 180s = 0.00341C/s = 3.41mA for 3 mins
    (2) Load = 0.614439C for 60s = 0.01024C/s = 10.24mA for 1 min
    (Repeat)

Then the average load is:

    Average load = 2 * 0.614439C for 240s = 0.00512C/s = 5.12mA
    Therefore battery should last 2600mAh / 5.12mA = 507.8 hours

Measuring current draw with LTC4150 + ESP-12E

Image
My LTC4150 Coulomb counter has finally arrived!

For testing, I hooked up the unit to the spare ESP-12E I have lying around:


All the jumpers on the LTC4150 are soldered (SJ1 = interrupt-driven counting; SJ2, SJ3 = 3.3V circuit).

The code for driving the Coulomb counter is as follows:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66#include <Time.h>#include <TimeLib.h>constint BATTERY_CAPS =2300; const byte INT_PIN = D1; constfloat INT_TO_COULUMB =0.614439; bool trigger =false, init_done =false; unsignedlong total_time =0, total_interrupts =0; volatileunsignedlong num_interrupts =0; volatileunsignedlong time1 =0, time2 =0; voiddebug(constchar*format, ...) { char buf[256]; va_list ap; va_start(ap, format); vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); Serial.println(buf); } voidhandleInterrupt() { if (time1…

ESPCLOCK2, Part 3 - Reading VCC using interrupt

In this post, I read the VCC voltage level to the ATtiny85 and save certain state values to the EEPROM if the voltage level is found to fall below a certain value. But this way of reading the VCC uses delay() and is found to interfere with I2C communication. So a better way needs to be found.

After much experimentation, I found the following code to work reliably:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17volatile byte adc_countdown =5; volatilebool adc_ready =false; // adc_countdown is decremented elsewhere every secondif (adc_ready) { adc_ready =false; vcc =1125300L/ ADC; // Calculate VCC (in mV); 1125300 = 1.1*1023*1000 adc_countdown =5; } elseif (!bit_is_set(ADCSRA, ADSC) && adc_countdown ==0) { ADMUX = bit(MUX3) | bit(MUX2); // Measure VCC using internal bandgap as reference ADCSRA = bit(ADEN) | bit(ADSC) | bit(ADIE) | bit(ADPS2) | bit(ADPS1) | bit(ADPS0); // Start conversion } ISR(ADC_vect) { adc_ready =true; }
Initially, the ADC register is read in…

ESPCLOCK2, Part 2 - Interrupt-driven time keeping

Due to the time sensitive nature of I2C, everything we do in the ATtiny85 has to be interrupt-driven. We cannot use any delay() in the code.

The ATtiny85 has two timers, Timer0 and Timer1. We will use Timer0 to drive the clock pins, and Timer1 to keep time.

First we configure Timer1 to interrupt every second:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15#define TIMER1_PRESCALER 4096#define OCR1A_DEFVAL ((byte)(F_CPU / (float)TIMER1_PRESCALER * 1.0) - 1)// Reset prescalers for Timer1 GTCCR |= bit(PSR1); // Setup Timer1 TCCR1 =0; TCNT1 =0; OCR1A = OCR1A_DEFVAL; OCR1C = OCR1A_DEFVAL; TCCR1 = bit(CTC1) | bit(CS13) | bit(CS12) | bit(CS10); // Start Timer1 in CTC mode; prescaler = 4096; // Interrupt on compare match with OCR1A TIMSK |= bit(OCIE1A);
Then in the ISR for Timer1, we simply keep track of the actual time (we call this nettime, or actual time from the network that we are trying to get the clock face to match).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18volatile byte nett…

ESPCLOCK2, Part 1 - Getting timezone and local time

I am almost done with the implementation of ESPCLOCK2, so I will start a series of posts detailing the obstacles encountered and lessons learnt from the project.

The original ESPCLOCK obtains the geolocation of the clock during setup from the user's browser, and use the geolocation to convert to local time using Google's Timezone API. As noted here, this approach hits a major snag as recent browsers started to restrict the use of navigator.geolocation.getCurrentPosition()to Javascript running from SSL-secured websites. Since it would be extremely unlikely for WifiManager to support SSL hosting, another approach is needed.

After considering the alternatives, I think the simplest approach will be to host a simple PHP script on a server. This has several advantages: 1) There are no API keys to muck with. All timezone APIs I looked at require you to register for some kind of API key. 2) You won't be affected by API changes, or the service shutting down. You can run the script …

Link dump for information regarding Lavet motor control in analog clocks

Experiments with Circadian Rhythm Fasting (Update)

After my 30-day normal diet brought my weight up to the 74+kg territory, I went on the Circadian Rhythm Fasting diet for 60 days. Here are the results:
There is the same downward trend that I saw in the original 30-day experiment, But it seems to hit a floor in the 71kg territory. Maybe if this goes on long enough, I will be able to push through the barrier, but at the time of writing this up, I have actually gone through another 30 days or so of the diet, and my weight has stayed around the 71kg range. With the 5-2 Fast Diet, I have seen small but persistent weight loss all the way down to the 65kg territory at one point (took about 9 months).

So in summary, I think the CRF diet is an easy and pain-free way of maintaining one's weight, but if the main objective is weight loss, the 5-2 Fast Diet might be more effective, albeit more painful way of achieving one's goal.

Switching ATtiny core

I was not getting reliable I2C communication between the D1 mini and the ATtiny85, starting with TinyWireS, then moving on to the more developed TinyWire. I would send out 5 bytes from the D1 mini, then try to get 16 bytes back. It will start off well, but a few hours later, something will break and the D1 mini will start getting 0xFFs from the ATtiny85.

Then I found "ATtinyCore" by Spence Konde (V1.1.5), which appears to be a more mature ATtiny core compared to the "attiny" core by David Mellis (V1.0.2). It has integrated I2C support that manifests as the familiar "Wire" library. The installation instructions are here. I did a manual upgrade to the latest git version as some I2C issues have been fixed in the V1.1.6 milestone but have not been officially released. As recommended by the instructions, I bumped up the Arduino version to 1.8.6.

Finally, happy to see reliable I2C communication between the 2 components! The communication has been going non-stop…

geolocation.getCurrentPosition() only works with HTTPS

Just found out that more recent web browsers requires a SSL-secured (HTTPS) site to host the target script before navigator.geolocation.getCurrentPosition() will work. Otherwise, the following error will be emitted:
getCurrentPosition() and watchPosition() are deprecated on insecure origins, and support will be removed in the future. You should consider switching your application to a secure origin, such as HTTPS. See goo.gl/rStTGz for more details. This creates a problem for the original ESPCLOCK code. Since WifiManager does not support hosting a HTTPS captive portal, the logic for capturing the user's geolocation (and hence the timezone) no longer works.

Back to the drawing board...

Programming the WeMos D1 mini

Image
So I went ahead and ordered the WeMos D1 Mini (now called Lolin D1 Mini). It's a diminutive ESP8266 dev board with reportedly very low deep sleep current draw.

To program this board, the CH340 driver needs to be installed in order to access the board via USB.


Then select the corresponding board type in Arduino. No code change is necessary.

Hooking up USBasp programmer to the ATTiny85

Image
After putting off the ESPClock project for quite a looooong while, I finally decided to pick up the project again.

But the Arduino Uno board had been siphoned off by my eldest son for one of his science projects, so I decided to pick up a cheap USPasp clone off eBay to help with programming the ATTiny85.


First off is to connect the 2x5 ISP header to the ATTiny85. It was a bit confusing at first, but after some research, here are the connections required.
I plugged the wires into the 10-pin ISP header, and soldered the other ends to two 1x4 male headers in the correct order. That way, I can just plug the male headers into the breadboard alongside the ATTiny85 quite easily to start programming it without too much fiddling.


I had to strengthen the soldering with some hot glue to prevent the wires from breaking off the headers when plugging them in and out repeatedly. Works a treat so far.

Then choose "USBasp" as the programmer under the Arduino IDE:


I was getting this error whe…

Experiments with Circadian Rhythm Fasting

I have been doing the Fast Diet for a couple of years now. It works wonderfully, without having to buy any special equipment or supplement, which is fantastic. However, even after all this time, the diet days still need getting some use to. This is especially true when I am on "maintenance" mode, which means you fast once instead of twice a week. Because I am doing it less frequently, the diet day becomes harder because the body takes more time to adjust.

Some time back, I came across an article talking about Circadian Rhythm Fasting, popularized by Dr Jason Fung and many others. Here's a more recent article on the topic. The main idea is to restrict eating to a small window of the day, effectively fasting for the rest of the time. Like the Fast Diet, it does not require any special equipment or supplement, and is very flexible with the timing. For example, you can choose to start at 7am and end at 3pm, or start at 12pm and end at 6pm. However, the common guideline is to…

MIUI: Connecting to MJX Bugs B2W

Image
I recently purchased the MJX Bugs B2W drone, and had a lot of difficulty connecting to the onboard 5GHz WIFI camera from the Bugs Go app. I finally figured out the solution, and it was totally unexpected, so I thought I'd share it here for anyone faced with the same issue.

I am using the Redmi Note 4 to connect, and that phone supported 5GHz WIFI, so I had no problem connecting to the drone hotspot. But when I launched the Bugs Go app, I keep getting the "Aircraft not connected" message. I tried a couple of stuff, including resetting the 5GHz channel used by the drone (by pressing on the "Camera" button on the remote controller for about 8 seconds, until you hear 3 beeps). Nothing worked.

Then I realized when I connect to the drone hotspot and run Bugs Go, I see this message:


It didn't occur to me initally to tap on the message, because I already knew the drone hotspot had no Internet acess. So out of desperation, I tapped on the message:


By selecting "…