Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Tuesday, 8 October 2024

Converting iCloud contacts for Outlook


iCloud to Outlook Contact conversion

At work, we need to more or less wipe all iphones clear and put them under control of a new MDM. That significantly limits what the users can do with their phones, but should strengthen security.

/* German Title: iCloud Kontakte in Outlook importieren */

The trouble is that some people have hundreds of contacts stored locally on their phones, which have been synched to their iCloud. While this is highly questionable from a GDPR point of view, that makes ist rather easy to export the contacts in vcf format as vcards.

The downside: Outlook only reads the first entry in the file. And the character encoding did not match our Outlook settings, so it screwed up the German umlauts.

So I needed to do two things:

  • split the one large vcf file into over a hundred individual ones
  • fix the character encoding, so it does not break the umlauts

Pretty easy to do so with a few lines of Python code:

 import os  
 def split_vcf(file_path):  
   with open(file_path, 'r', encoding='utf-8') as file:  
     vcf_content = file.read()  
   vcards = vcf_content.split('END:VCARD')  
   vcards = [vcard + 'END:VCARD' for vcard in vcards if vcard.strip()]  
   output_dir = 'split_vcards'  
   os.makedirs(output_dir, exist_ok=True)  
   for i, vcard in enumerate(vcards):  
     output_file = os.path.join(output_dir, f'{i+1}.vcf')  
     with open(output_file, 'w', encoding='cp1252') as file:  
       file.write(vcard)  
 if __name__ == '__main__':  
   split_vcf('iCloud-vCards.vcf')  


That will read a file "iCloud-vCards.vcf" and split the entries into individual files in a "split_vcards" subdirecrory. While changing the encoding from utf-8 to "Western European (Windows)", i.e. cp1252.

It still does a few strange things like not setting the preferred telephone automatically, but all in all, nothing appears to get lost.

Tuesday, 2 October 2018

How to build a low cost applause-o-meter

Building an applause-o-meter with a WS1361

1)The task

When a friend asked me if I could build an applause-o-meter (clap-o-meter, clapometer, applausemeter) for a concert, I thought that should be quick and painless. - I was wrong.
But not knowing what one is up against can be a blessing. And so it went:
Detail of the application: Progress bars as bargraphs


2) The Hardware

So I bought the cheapest sound level meter I could find at my favourite Chinese seller that sported an USB interface: The Wensn WS1361, also sold as HY1361.
See this article about the driver setup to get it up and running with the original software in this blog post.
The other things needed for the applause-o-meter are a projector and a computer running a current version of Windows.

3) The software

For my purposes, the supplied software is pretty useless. So I set out to write my own software for reading the meter.

3.1 libusb-win32 vs libusb

While the SoundPCLink  software relies on libusb-win32, I found a fantastic project for using the libusb at libusb.info. Full support for Visual Studio 2017. - Very handy.
You need to change the driver for the WS1361 from libusb-win32 to libusb (Winusb) with Zadig.
Change the driver with Zadig
If you don't see the WS1361 listed, check the "list all devices" option.

3.2 Simple c++ sound level reader

After a little time it took to understand the library, I wrote a very simple command line tool to read a single db value from the meter:


 /*  
  * ReadSoundMeter: Read DB Value from WS1361 / HY1361 sound level meter  
  * 2018-09 by AReResearch (Andy Reischle)  
  * www.areresearch.net  
  * Inspiration and many lines of code taken from  
  * Pete Batard <pete@akeo.ie> 's example code to libusb, xusb.c  
  */  
 #include "pch.h"  
 #include <stdio.h>  
 #include <stdint.h>  
 #include <stdlib.h>  
 #include <string.h>  
 #include <stdarg.h>  
 #include <C:\Buffer\SoundMeter\libusb-master\libusb\libusb.h>  
 #define CALL_CHECK_CLOSE(fcall, hdl) do { int _r=fcall; if (_r < 0) { libusb_close(hdl); ERR_EXIT(_r); } } while (0)  
 #define ERR_EXIT(errcode) do { perr("  %s\n", libusb_strerror((enum libusb_error)errcode)); return -1; } while (0)  
 #if defined(_WIN32)  
 #define msleep(msecs) Sleep(msecs)  
 #else  
 #include <time.h>  
 #define msleep(msecs) nanosleep(&(struct timespec){msecs / 1000, (msecs * 1000000) % 1000000000UL}, NULL);  
 #endif  
  // Future versions of libusb will use usb_interface instead of interface  
  // in libusb_config_descriptor => cater for that  
 #define usb_interface interface  
 int r;  
 static uint16_t VID = 0x16C0;  
 static uint16_t PID = 0x05DC;  
 static void perr(char const *format, ...)  
 {  
      va_list args;  
      va_start(args, format);  
      vfprintf(stderr, format, args);  
      va_end(args);  
 }  
 static double test_device(uint16_t vid, uint16_t pid)  
 {  
      uint8_t resultat[2];  
      libusb_device_handle *handle;  
        
      handle = libusb_open_device_with_vid_pid(NULL, vid, pid);  
        
      if (handle == NULL) {  
           perr(" Failed.\n");  
           return -1;  
      }  
        
      r = libusb_control_transfer(handle, 0xC0, 0x04, 0, 0, resultat, sizeof(resultat), 1000);  
      if (r < 0) {  
           fprintf(stderr, "Error during control transfer: %s\n",  
                libusb_error_name(r));  
      }  

      libusb_close(handle);  
        
      return ((resultat[0] + ((resultat[1] & 3) * 256)) * 0.1 + 30);  
 }  
 int main(int argc, char** argv)  
 {  
      libusb_context *ctx = NULL; //a libusb session  
      r = libusb_init(NULL);  
      if (r < 0)  
           return r;  
    }  
      printf("%f\n", test_device(VID, PID));  
      libusb_exit(NULL);  
 }  

3.3 And some visual basic

Writing a Windows forms application in C++ turned out a lot harder than expected. It feels like Microsoft had never even intended that to go smoothly.
So I took an extremely ugly approach to call the above command line tool and read it's output into a visual basic windows forms application. The way I did that eats half the CPU power of a brand new i5 machine.
But I needed a quick solution. After the better half of a night of coding, I had a working version.

applause-o-meter GUI (German)
As you might see from the screenshot (German, sorry), the idea is to have three contesting pieces of music per group and three groups.
The audience can "vote" one of the three pieces of each group to be played fully that evening.

4) The performance

A few brief words explaining voting procedure was all that it took. This was the first time that had been done in church music, and as a part of a city-wide, cultural event, it was received very well by the audience.

Showing the results after the performance
Unsurprisingly, J.S. Bach's Toccata in d-minor made it 1st among the 12 pieces.



PS: The visual basic code is quite ugly and needs some tidying before publication. If you are in dire need of a clap-o-meter, please leave a note in the comments and I will make the code available regardless of it's shortcomings.



Intersting WS1361 links:


Sunday, 2 April 2017

#arduinoD17 project: Arduino decodes the BSIDE ADM20 serial infrared protocol

##### THIS  POST  NEEDS  MORE WORK ####

Due to the time constraints, my video on decoding the protocol is not quite as polished as usual, but possibly more "authentic".

A look inside

Although it did not make it into my review video, I one of the first things I did was taking the meter apart. Apart from making sure that the basic safety features were in place, the thing that caught my eye was the USB Interface.
It is completely a self-contained module powered by the PC over the USB cable. The CH340 USB-UART bridge chip should be familiar to anyone using Arduino knockoffs.
The module, of course is completly insulated against the meter, which sends a stream of serial data through a single infrared LED.


Where to get the multimeter
The meter is available under several names:

Other materials

... and as I have been asked about the cute scope I used:
It is around 20€ and easy to build. There is a firmware update available from JYE Tech. I'll do a video about the process.

The protocol

As mentioned previously, the meter sends a continuous stream of data at a rate of 2400 baud. This can be captured easily.
The only part of the protocol I figured out so far are the actual digits. I still have too look into the units and the "minus". The protocol is not "human-friendly", but feels like it is derived from the meter's data stream to the LCD module.


Code

So here is the code for you to try:

 //Meater-Reater (Arduino version)  
 //Arduino infrared interface for BSIDE ADM20 Multimeter  
 //see www.AReResearch.net for details  
 //20170401 by Andy Reischle  
 #include <SoftwareSerial.h>  
 // Softserial only required during development to preserve  
 // serial debugging. When done, move to hardware UART  
 SoftwareSerial swSer(8,9);  
 uint8_t inByte = 0;  
 int initvalues[6] = {0xAA, 0x55, 0x52, 0x24, 0x01, 0x10};  
 int line[22];  
 int measures[4];  
 int counter = 0;  
 int dval;  
 String result = "";  
 bool initstart = false;  
 void setup() {  
  Serial.begin(9600);  
  swSer.begin(2400);  
  Serial.println("\nStart reading from Soft UART");  
        }  
 void loop() {  
  // Serial.println("Doing my thing");  
  Serial.println(measure());  
  delay(5000);  
 }  
 String measure() {  
  initstart = true;  
  result="";  
  counter=0;  
  swSer.flush();  
  while (initstart) {  
   if (swSer.available() > 0)  
   {  
   inByte = swSer.read();  
 // Serial.print(inByte, HEX);  
 //  Serial.print(" ");  
   if (counter >5 )  
    {  
     measures[counter-6] = inByte;  
     //Serial.print("result: ");  
     //Serial.println(inByte, HEX);  
     if (counter == 9)  
      {  
       if (measures[3] > 128) result = result + ".";  
       result = result + displval(measures[3]);  
       if (measures[2] > 128) result = result + ".";  
       result = result + displval(measures[2]);  
       if (measures[1] > 128) result = result + ".";  
       result = result + displval(measures[1]);  
       if (measures[0] > 128) result = result + ".";  
       result = result + displval(measures[0]);  
       // Serial.println (result);  
       return result;  
       initstart = false;  
      }  
     counter++;  
     if (counter > 9)  
      {  
       counter = 0;  
       result="";  
       initstart = false;  
      }  
    }  
   else if (inByte == initvalues[counter])  
    {  
     counter ++;  
    }  
                 }  
       }  
 }  
 //  
 // Convert display values  
 //  
 int displval(int dval)  
 {  
 if (dval > 128)  
  {  
   dval = dval -128;  
  }  
  if (dval == 95) return 0;  
  if (dval == 6) return 1;  
  if (dval == 107) return 2;  
  if (dval == 47) return 3;  
  if (dval == 54) return 4;  
  if (dval == 61) return 5;  
  if (dval == 125) return 6;  
  if (dval == 7) return 7;  
  if (dval == 127) return 8;  
  if (dval == 63) return 9;  
  if (dval == 0) return 0;  
 }  

Friday, 10 March 2017

Detect CO with a MQ-7 sensor module

How to detect carbon monoxide with a MQ-7 sensor module

How gas sensors work

I found an excellent thesis paper on how Tin Dioxide (SnO2) gas sensors work here. It also goes into the details of it's temperature dependency. (See details further down)

Video

Watch my video on the tests here.

The module from ICStation

You can get this module here. (Use code andyics for 15% off your order)
The intended mode of operation is to apply 5V to the module and either read analog values from AOUT or set the threshold of the comparator to the desired value and read from the DOUT pin if it has tripped.


The terminals

Comparator and trimmer


It seems to me that ICStation treats all MQ-series sensors the same way. But the MQ-7 is different from the rest. According to the data sheet, it gives the best results on the following cycle:

  • Pre-heat sensor for 48h 
  • Heat heater with 5V for 60 seconds
  • Heat at 1.4V for 90 seconds
  • Read the sensor near the end of the 90 seconds
On 5V alone, the module does "sortof" work.

You can see me breathing at the sensor

I am quite sure there is no siginificant quantity of CO in my breath. And I not a smoker. The sensor reacts to a wide range of gases, as well as moisture and ambient temperature,

Tricking the module into datasheet-like conditions

To build this, you need the following components:



The IRLZ34N is a very common N-Channel MosFET what already has a very low (0.046 Ohm) source-drain resistance with 5V at the gate. It can handle currents way beyond our reqirements for the flimsy heater on the module.
The heater can run on DC or AC, so PWM should be ok. I can then set the duty cycle of the PWM so that it is the equivalent of 1.4V. (See code below.)


The 10k resistor is optional

The setup with the "switching" mosfet.
Setup with Mosfet



The Arduino code

The code for the "proper" usage cycle:

 /*  
 MQ-7 cheater  
 Uses PWM and an N-Channel MosFET to trick an ICSTATION MQ-7 CO detector  
 into measuring CO according to the datasheet of the manufaturer.  
 */  
 int sensorPin = A0;  // select the input pin for the CO sensor  
 int sensorValue = 0; // variable to store the value coming from the sensor  
 // Initial setup  
 void setup() {  
  // initialize digital pin LED_BUILTIN as an output  
  pinMode(LED_BUILTIN, OUTPUT);  
  // initialize the serial port  
  Serial.begin(9600);  
 }  
 // the loop function runs over and over again forever  
 void loop() {  
  analogWrite(LED_BUILTIN, 255);  // turn the heater fully on  
  delay(60000);            // heat for 60 second  
 // now reduce the heating power  
  analogWrite(LED_BUILTIN, 72);  // turn the heater to approx 1,4V  
  delay(90000);            // wait for 90 seconds  
 // we need to read the sensor at 5V, but must not let it heat up. So hurry!  
  digitalWrite(LED_BUILTIN, HIGH);  
  delay (50); //don't know how long to wait without heating up too much. Getting an analog read apparently takes 100uSec  
   // read the value from the sensor:  
  sensorValue = analogRead(sensorPin);  
  Serial.println(sensorValue);  
 }  

Increased sensitivity

Under the same conditions (candle suffocated under jar), the FET-Pulsed version showed a significantly higher peak.
FET-Pulsed heater

Heater on 5v constantly
While the pulsed version of the detector has a slower detection rate (once every 2.5 minutes), the signal's signal-to-noise ratio is signigicantly better (400:14 vs 220:28), resulting in better sensitivity.

Other options:

Cut the traces on the PCB and rewire, so the heater and the sensor don't run from the same power source. (I.e. run cycle the heating element at 5/1.4, while keeping constant 5V on the sensing element's voltage divider)

Friday, 24 February 2017

Hacking the BSIDE ADM20 Multimeter - Software

BSIDE ADM20 hack 1: Software

How I got into this

When I worked on a review of a battery charger, I came accross some potential issues that I had to investigate things more thoroughly. I needed a multimeter to record the charge curves.
So my contact at Gearbest sent me this BSide ADM20 Multimeter. This has a built-in USB interface to display and record mesaurements on the PC.
Values imported into LibreOffice Calc
It turned out I quite like the meter. See my review video here. (Hardware-hack will follow) The software however was rather basic and wouldn't allow to set a sample rate or measurement duration.

The meter is available under several names:

I already had a look inside the meter and see pretty cool options to turn this into an IoT device. But let's not jump to conclusions. Some more work needs to go into that and I have only focussed on the software side here.

Plug&Play

Fortunately it is pretty obvious how the meter communicates with (or rather "to") the PC:
A new COM port appears, presented through the well known CH340 USB-to-SERIAL bridge driver.
And you thought COM-Ports were a thing of the past
If you then fire up the software (DMM Data logger) that came with the meter, you're good to go.

Original Software

Nooo! Boooooooring!!!!

A look at the protocol

Pretty obvious that I should see something when I start a a terminal program like TeraTerm od Putty.
In part 2 of this post, you'll see that this is strictly a one-way communication. So we can't talk back to the meter.

  • The port speed is 2400 baud.
  • There is no CR or LF at the end of each data set (see below)
  • The usual 8n1 seems to apply
  • Continuous stream of data: no xon/xoff
  • No return channel

With the width set properly, TeraTerm's hex mode shows a pattern:
The 5Fs are the Zeroes, the DF has the decimal point

Whatever I do, the transmission always starts with a series of HEX values: AA5552240110
followed by four bytes that change when stuff moves on the display. I could map the values to the following displayed digits: (excerpt from my visual basic prog)

        If SerVal = 95 Then measured = 0
        If SerVal = 6 Then measured = 1
        If SerVal = 107 Then measured = 2
        If SerVal = 47 Then measured = 3
        If SerVal = 54 Then measured = 4
        If SerVal = 61 Then measured = 5
        If SerVal = 125 Then measured = 6
        If SerVal = 7 Then measured = 7
        If SerVal = 127 Then measured = 8
        If SerVal = 63 Then measured = 9

It turns out that the most significant bit is the decimal point, the other bits map to the seven segments. It also sends the measured unit and the polarity further back in the data stream. Up to now I choose to ignore all of that.

The four bytes with the four digits are in reverse order, of course, for more programming fun.

So my VisualBasic program listens for the "AA555224110" sequence and then decodes the four following bytes.

I suspect that the data stream is derived from the communication with the display driver, as many bits in the data stream can directly be mapped to segments on the display.

More on those details in the second part where I will look at the hardware of both the meter and it's communication.

First try in VisualBasic

No decimal point yet.
That was once a 9v battery

If you want to have a go at the experimental code, here is where I left off for the moment:

 Imports System.Threading.Tasks  
 Imports System.Timers  
 Imports System.IO  
 Imports System.IO.Ports  
 Imports System.Threading  
 Public Class Form1  
   Dim datensatz As String  
   Dim rohwert As Integer  
   Dim werte(22) As Integer  
   Dim decodewerte(4) As Integer  
   Dim recorddata As Boolean = False  
   Dim i As Integer = 0  
   Delegate Sub DataDelegate(ByVal sdata As Integer)  
   REM Define the method (Function) that will be called by the Invoke method   
   Private Sub PrintData(ByVal sdata As Integer)  
     Dim startsequence As String = "AA555224110"  
     Dim tmpchar As String  
     Dim str As Integer  
     Dim measured As Integer  
     Dim x As Integer  
     If recorddata Then  
       werte(i) = sdata  
       Console.Write("I= ")  
       Console.WriteLine(i)  
       If i = 4 Then  
         recorddata = False  
         i = 0  
         tmpchar = Hex(werte(1))  
         REM Console.WriteLine(werte(1))  
         x = DecodeValue(werte(1))  
         decodewerte(1) = x  
         Console.WriteLine(x)  
         Label2.Text = x  
         tmpchar = Hex(werte(2))  
         REM Console.WriteLine(werte(2))  
         x = DecodeValue(werte(2))  
         decodewerte(2) = x  
         Console.WriteLine(x)  
         Label3.Text = x  
         tmpchar = Hex(werte(3))  
         REM Console.WriteLine(werte(3))  
         x = DecodeValue(werte(3))  
         decodewerte(3) = x  
         Console.WriteLine(x)  
         Label4.Text = x  
         tmpchar = Hex(werte(4))  
         REM Console.WriteLine(werte(4))  
         x = DecodeValue(werte(4))  
         decodewerte(4) = x  
         Console.WriteLine(x)  
         Label5.Text = x  
         TextBox1.Text = CStr(decodewerte(4)) & CStr(decodewerte(3)) & CStr(decodewerte(2)) & CStr(decodewerte(1))  
         sp.DiscardInBuffer()  
       End If  
       i = i + 1  
     End If  
     tmpchar = Hex(sdata)  
     Label1.Text = tmpchar  
     datensatz = datensatz + tmpchar  
     Console.WriteLine(datensatz)  
     If (datensatz.Contains(startsequence)) Then  
       REM Console.WriteLine("Got Header")  
       datensatz = ""  
       recorddata = True  
     End If  
   End Sub  
   Public Sub New()  
     ' This call is required by the designer.  
     InitializeComponent()  
     ' Add any initialization after the InitializeComponent() call.  
   End Sub  
   Dim WithEvents sp As New SerialPort  
   Private Sub GetSerialPortNames()  
     sp.BaudRate = 2400  
     sp.PortName = "COM3"  
     sp.Open()  
     sp.DataBits = 8  
     sp.Parity = Parity.None  
     sp.StopBits = StopBits.One  
     sp.Handshake = Handshake.None  
     REM sp.Encoding = System.Text.Encoding.Default  
     sp.Encoding = System.Text.Encoding.Default  
   End Sub  
   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load  
     GetSerialPortNames()  
   End Sub  
   Private Sub SerialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles sp.DataReceived  
     Dim str As Integer  
     REM Dim str2 As Char  
     str = sp.ReadChar()  
     REM Console.WriteLine(str)  
     REM str2 = Convert.ToChar(str)  
     Dim adre As New DataDelegate(AddressOf PrintData)  
     Me.Invoke(adre, str)  
   End Sub  
   Function DecodeValue(ByVal SerVal As Integer)  
     Dim decimalpoint As Boolean = 0  
     Dim measured As Integer  
     If SerVal > 128 Then  
       SerVal = SerVal - 128  
       decimalpoint = True  
     End If  
     measured = 99  
     If SerVal = 95 Then measured = 0  
     If SerVal = 6 Then measured = 1  
     If SerVal = 107 Then measured = 2  
     If SerVal = 47 Then measured = 3  
     If SerVal = 54 Then measured = 4  
     If SerVal = 61 Then measured = 5  
     If SerVal = 125 Then measured = 6  
     If SerVal = 7 Then measured = 7  
     If SerVal = 127 Then measured = 8  
     If SerVal = 63 Then measured = 9  
     If SerVal = 8097 Then measured = 7  
     If SerVal = 8096 Then measured = 1  
     If SerVal = 0 Then measured = 0  
     Console.Write("Decoder got a: ")  
     Console.Write(SerVal)  
     Console.Write(" decoded as: ")  
     Console.WriteLine(measured)  
     Return measured  
   End Function  
 End Class  

If you have done work on hard- or software-hacking those meters please let me know.


Sunday, 14 August 2016

Mini ESP8266 dev board and a demo WiFi hack

I attended a one week network security training recently and taught end-user security awareness a little later. One outstanding topic in both trainings were weak WPA/WPA2 passwords.
I actually wanted to explore ways to use menues on my little I2C OLED display. So I set out to combine testing for weak WiFi passwords and findind a way to make easy to use menus.

But menues need buttons and there was no space left on my little breadboard between the NodeMCU dev module and the OLED. So I looked for smaller breadboard-ready ESP8266 dev modules and found this inexpensive ESP8266 Dev Mini Module.




Further research showed that this might be pretty much a knockoff of the Wemos D1 Mini, I hadn't seen before.
This board has a lot less pins as compared to a full NodeMCU dev board. But all the important ones seem to be there. The board came with a set of headers and I decided to make the USB stuff the bottom side, so I can see the LED on the ESP-12F module.
Top view: ESP-12f

Bottom view: USB
The USB drivers auto-installed on my Windows 10 machine.


So my first project with this board was a very simple WiFi security scanner that lists all available AccessPoints (excluding the invisible ones) and try to get in with a list of passwords stored in the SPIFFS file system.

Here is my video about both the module and the Wifi Security tester.


Fritzing schematic of the WiFi scanner
My motivation was to find out how to make a simple menue system. The current implementations has quite a few shortcommings. Eg: The list of WiFi targets can only be a few items long, and does not scroll. Simpley because the number of  networks visible from my lab was never longer than that.
I use interrupts (falling edge) on the GPIO pins to trigger functions that increment or decrement the menu selection bar.

Good WPA/WPA2 password lists are shipped with Kali linux, but these are *WAY* too big to fit on the module's file system. You have to ressort to "educated guessing" there,

If you are looking for the code for the Wifi-Security tester, it is up on my GitHub repository. It still needs quite a lot of cleanup and a few functions should be rewritten, so beware!


Wednesday, 6 July 2016

GMail notifier with ESP8266 / NodeMCU


A while back I investigated the use of NodeMCU with GMail. One result was this script to send mails over GMail. The other aspect I initially didn't fully investigate was the atom feed offered by GMail.
Looks like we have unread mail
If you haven't watched the video yet, here it is.

In the code below, I use that feed to retrieve the number of unread elements from the inbox.
Apart from the Lua code, you also need to place the two files with the mailbox icons on NodeMCU's file system:

Mailoff-file: here
Mailon-file: here

That is what it looks like in action:



I recommend "esplorer" to copy the files to the ESP8266 module.

 -- ESP8266 NodeMCU  
 -- GMail Notifier  
 -- 2016/07 Andy Reischle  
 -- www.AReResearch.net  
 -- Graphics handling and conversion  
 -- adapted from Daniel Eichhorns blog  
 -- http://blog.squix.org/2015/05/esp8266-nodemcu-how-to-create-xbm.html  
 --  
 -- To see this script in action, see:  
 -- https://youtu.be/IVxJosLZCXs  
 wifi.setmode(wifi.STATION)  
 wifi.sta.config("YOUR-SSID","YOUR-WIFIPASS")  
 wifi.sta.connect()  
 -- setup I2c and connect display  
 function init_i2c_display()  
    -- SDA and SCL can be assigned freely to available GPIOs  
    sda = 5 -- GPIO14  
    scl = 6 -- GPIO12  
    sla = 0x3c  
    i2c.setup(0, sda, scl, i2c.SLOW)  
    disp = u8g.ssd1306_128x64_i2c(sla)  
 end  
 function xbm_picture()  
    disp:setFont(u8g.font_6x10)  
    disp:drawStr( 0, 62, "Google Mail Notifier")  
    disp:drawXBM( 10, 5, 32, 32, xbm_data )  
    disp:drawStr (65,30, unread .. " unread")  
 end  
 function bitmap_mailon(delay)  
    file.open("mailon", "r")  
    xbm_data = file.read()  
    file.close()  
    disp:firstPage()  
    repeat  
       xbm_picture()  
    until disp:nextPage() == false  
    tmr.wdclr()  
 end  
 function bitmap_mailoff(delay)  
    file.open("mailoff", "r")  
    xbm_data = file.read()  
    file.close()  
    disp:firstPage()  
    repeat  
       xbm_picture()  
    until disp:nextPage() == false  
    tmr.wdclr()  
 end  
 init_i2c_display()  
 function checkmail()  
 user="YOURADDRESS@GOOGLEMAIL.COM"  
 pass="YOURGMAILPASSWD"  
 b64 = crypto.toBase64(user .. ":" .. pass)  
 -- print (b64)  
 local LED_PIN1 = 4   
 gpio.mode(LED_PIN1, gpio.OUTPUT)  
 conn=net.createConnection(net.TCP, 1)  
 conn:on("receive", function(sck, c)  
 -- print(c)  
 start1,stop1=string.find(c,"<fullcount>")  
 start2,stop2=string.find(c,"</fullcount>")  
 if start1 then  
   unread=string.sub(c,stop1+1,start2-1)  
   print ("Found " .. unread .. " unread Mails.")  
    if tonumber(unread) > 0 then  
         gpio.write(LED_PIN1, gpio.LOW)  
         conn:close() -- we got what we came for, so close  
         bitmap_mailon()  
     else   
         gpio.write(LED_PIN1, gpio.HIGH)   
         conn:close() -- no Mail, so close  
         bitmap_mailoff()  
    end  
  end  
 end )  
 conn:on("connection", function(conn)  
    print("connected")  
    conn:send("GET https://mail.google.com/mail/feed/atom/ HTTP/1.1\r\n" ..  
        "Host: mail.google.com\r\n"..   
        "Authorization: Basic " .. b64 .. "\r\n" ..  
       "User-Agent: Mozilla/4.0 (compatible; esp8266 Lua;)"..  
        "\r\n\r\n")   
 end )  
 conn:on("disconnection", function(conn) print("disconnected") end )  
 conn:connect(443,"mail.google.com")  
 end  
 tmr.alarm(0,30000,tmr.ALARM_AUTO,checkmail)  

Not much stuff is needed for that little project:


Assembly is done in no time at all. Just connect power and I2C leads. (For me, this works without pull-up resistors.)

Not a lot to do.






Friday, 15 April 2016

How to send emails via gmail from an ESP8266 running NodeMCU

How to send smtp emails via gmail from an ESP8266 running NodeMCU

SSL Support

When I found out about NodeMCU's SSL support (yes: I am very late to the party), one of the first things to try was sending mails. There are web services that will do that for you, but I don't like to have yet another party involved. So I needed SMTP through an SSL connection,
There is an implementation in C here in the forums, but I couldn not find anything ready-made for NodeMCU.
What I did find, was a very nicely written LUA script from "Miguel" in the NodeMCU LUA examples. This only needed a few minor modifications to run on the current DEV-version of NodeMCU:

NodeMCU custom build by frightanic.com
branch: dev
commit: 3f418f995cfccbaf7a745e65c81251c4c50759e6
SSL: true
modules: adc,crypto,file,gpio,http,i2c,net,node,tmr,u8g,uart,wifi
 build built on: 2016-04-11 20:31
 powered by Lua 5.1.4 on SDK 1.5.1(e67da894)

Not all of the modules are really used in this script, of course. So you can trim that down a bit.

Example mail on iPhone


Send an e-mail

With all of that in place, it only took a few minutes to have the first mail sent from my ESP8266-DEV board.
So here is the code for you to try:


 -- Modifications for GMAIL by Andreas "Andy" Reischle: www.AReResearch.net  
 -- See https://support.google.com/a/answer/176600?hl=de for details on smtp with gmail  
 -- Now that NodeMCU has working SSL support, we can also talk to email services that  
 -- require encryption.   
 -- Caveat: I have not looked into the SSL implementation, but I suspect it is vulnerable  
 -- to man-in-the-middle attacks as the client doesn't check the server's certificate.  
 -- 20160415 ARe  
 --------Original Credits:  
 --------  
 ------- Working Example: https://www.youtube.com/watch?v=CcRbFIJ8aeU  
 ------- @description a basic SMTP email example. You must use an account which can provide unencrypted authenticated access.  
 ------- This example was tested with an AOL and Time Warner email accounts. GMail does not offer unecrypted authenticated access.  
 ------- To obtain your email's SMTP server and port simply Google it e.g. [my email domain] SMTP settings  
 ------- For example for timewarner you'll get to this page http://www.timewarnercable.com/en/support/faqs/faqs-internet/e-mailacco/incoming-outgoing-server-addresses.html  
 ------- To Learn more about SMTP email visit:  
 ------- SMTP Commands Reference - http://www.samlogic.net/articles/smtp-commands-reference.htm  
 ------- See "SMTP transport example" in this page http://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol  
 ------- @author Miguel  
 --no longer required because it is part of the crypto module: require("base64")  
 -- The email and password from the account you want to send emails from  
 local MY_EMAIL = "YOURMAILADDRESS@gmail.com"  
 local EMAIL_PASSWORD = "YOURPASSWORD"  
 -- The SMTP server and port of your email provider.  
 -- If you don't know it google [my email provider] SMTP settings  
 local SMTP_SERVER = "smtp.gmail.com"  
 local SMTP_PORT = "465"  
 -- The account you want to send email to  
 local mail_to = "RECIPIENT@HISDOMAIN.COM"  
 -- Your access point's SSID and password  
 local SSID = "YOURWIFISSID"  
 local SSID_PASSWORD = "SECRET-I-WONT-TELL"  
 -- configure ESP as a station  
 wifi.setmode(wifi.STATION)  
 wifi.sta.config(SSID,SSID_PASSWORD)  
 wifi.sta.autoconnect(1)  
 -- These are global variables. Don't change their values  
 -- they will be changed in the functions below  
 local email_subject = ""  
 local email_body = ""  
 local count = 0  
 local smtp_socket = nil -- will be used as socket to email server  
 -- The display() function will be used to print the SMTP server's response  
 function display(sck,response)  
    print("Got a response: ")  
    print(response)  
 end  
 -- The do_next() function is used to send the SMTP commands to the SMTP server in the required sequence.  
 -- I was going to use socket callbacks but the code would not run callbacks after the first 3.  
 function do_next()  
       if(count == 0)then  
         count = count+1  
         local IP_ADDRESS = wifi.sta.getip()  
         print ("Send my IP: " .. IP_ADDRESS)  
         smtp_socket:send("HELO "..IP_ADDRESS.."\r\n")  
       elseif(count==1) then  
         count = count+1  
         smtp_socket:send("AUTH LOGIN\r\n")  
       elseif(count == 2) then  
         count = count + 1  
         smtp_socket:send(crypto.toBase64(MY_EMAIL).."\r\n")  
       elseif(count == 3) then  
         count = count + 1  
         smtp_socket:send(crypto.toBase64(EMAIL_PASSWORD).."\r\n")  
       elseif(count==4) then  
         count = count+1  
         smtp_socket:send("MAIL FROM:<" .. MY_EMAIL .. ">\r\n")  
       elseif(count==5) then  
         count = count+1  
         smtp_socket:send("RCPT TO:<" .. mail_to ..">\r\n")  
       elseif(count==6) then  
         count = count+1  
         smtp_socket:send("DATA\r\n")  
       elseif(count==7) then  
         count = count+1  
         local message = string.gsub(  
         "From: \"".. MY_EMAIL .."\"<"..MY_EMAIL..">\r\n" ..  
         "To: \"".. mail_to .. "\"<".. mail_to..">\r\n"..  
         "Subject: ".. email_subject .. "\r\n\r\n" ..  
         email_body,"\r\n.\r\n","")  
         smtp_socket:send(message.."\r\n.\r\n")  
       elseif(count==8) then  
         count = count+1  
          tmr.stop(0)  
          smtp_socket:send("QUIT\r\n")  
       else  
         smtp_socket:close()  
       end  
 end  
 -- The connectted() function is executed when the SMTP socket is connected to the SMTP server.  
 -- This function will create a timer to call the do_next function which will send the SMTP commands  
 -- in sequence, one by one, every 5000 seconds.   
 -- You can change the time to be smaller if that works for you, I used 5000ms just because.  
 function connected(sck)  
   print("Connected - Starting Timer")  
   tmr.alarm(0,5000,1,do_next)  
 end  
 -- @name send_email  
 -- @description Will initiated a socket connection to the SMTP server and trigger the connected() function  
 -- @param subject The email's subject  
 -- @param body The email's body  
 function send_email(subject,body)  
    count = 0  
    email_subject = subject  
    email_body = body  
    print ("Open Connection")  
    smtp_socket = net.createConnection(net.TCP,1)  
    smtp_socket:on("connection",connected)  
    smtp_socket:on("receive",display)  
    smtp_socket:connect(SMTP_PORT,SMTP_SERVER)  
 end  
 -- Send an email  
 print ("Sending started...")  
 send_email("ESP8266-GMailSender","Hi there!")  

This will need a little tidying, but will certainly make it into one of my projects.

Caveat:

NodeMCU's SSL implementation does currently not check the server's certificate. So I suspect man-in-the-middle attacks are easy.

Wednesday, 23 September 2015

Generic UDP proxy for NodeMCU / ESP8266 - Simple LUA DNS proxy

For a project that has been in the works for quite some time, I need a ESP8266 module to act as a DNS proxy. Other than on my very popular CaptiveIntraweb project, that simply lies to all DNS requests, I need real DNS lookups this time.

Getting my head slowly around the event driven nature of NodeMCU, the code turned into a very compact, generic UDP proxy or forwarder.
The script is completely unaware of the structure of the data and could be used to proxy all sorts of UDP data.

Here is my code:

 -- Simple DNS Proxy  
 -- 20150923 by Andy Reischle  
 -- Blog: www.AReResearch.net  
 -- Vids: www.youtube.com/AReResearch  
 --  
 -- Uses googles dns server 8.8.8.8  
 -- change to whatever suits you  
 cu=net.createConnection(net.UDP,0)  
 cu:on("receive",function(cu,c)   
   -- print("Got a reply")  
   s:send(c)  
   end)  
 s=net.createServer(net.UDP)  
 s:on("receive",function(s,d)  
   -- print ("Got a request!")  
   cu:connect(53,"8.8.8.8")  
   cu:send(d)   
   end)  
 s:listen(53)  

The ESP-module is connected to a WiFi AP, of course. The IP address of the ESP module is 192.168.1.74 and will be different, depending on your home DHCP server.

I can query DNS information through the module now:
me@raspberrypi:~$ nslookup
> server 192.168.1.74
Default server: 192.168.1.74
Address: 192.168.1.74#53
> www.areresearch.net
Server:         192.168.1.74
Address:        192.168.1.74#53

Non-authoritative answer:
www.areresearch.net     canonical name = ghs.google.com.
ghs.google.com  canonical name = ghs.l.google.com.
Name:   ghs.l.google.com
Address: 173.194.65.121
>
The corresponding tcpdump also looks very clean:

me@raspberrypi:~$ sudo tcpdump -i eth0 port 53
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
23:39:08.416736 IP noname.43027 > noname.domain: 56537+ A? www.areresearch.net. (37)
23:39:08.422009 IP noname.46122 > fritz.box.domain: 4560+ PTR? 58.1.168.192.in-addr.arpa. (43)
23:39:08.426857 IP fritz.box.domain > noname.46122: 4560* 1/0/0 PTR noname. (88)
23:39:08.428198 IP noname.45680 > fritz.box.domain: 56651+ PTR? 74.1.168.192.in-addr.arpa. (43)
23:39:08.430620 IP fritz.box.domain > noname.45680: 56651* 1/0/0 PTR noname. (88)
23:39:08.432969 IP noname.38314 > fritz.box.domain: 15794+ PTR? 1.1.168.192.in-addr.arpa. (42)
23:39:08.435987 IP fritz.box.domain > noname.38314: 15794* 1/0/0 PTR fritz.box. (89)
23:39:08.480105 IP noname.domain > noname.43027: 56537 3/0/0 CNAME ghs.google.com., CNAME ghs.l.google.com., A 173.194.65.121 (101)

While the rest of the project still requires a lot of tinkering, this little piece of the puzzle works nicely.


Friday, 18 September 2015

New CaptiveIntraweb release V3 - ESP8266 / NodeMCU based captive portal

After a few hours of brushing things up, I released a new version of my CaptiveIntraweb on  my GitHub repo.
Apart from a few bug fixes, an all-new init.lua now does the Wifi Setup, compiles the LUA files and starts the servers. It also features an option to prevent the TCP and UDP servers from starting.

I did most of the development on my new, very user friendly V3 NodeMcu Lua WIFI Development Board I got directly from the PRC from Banggood. That board has 4MByte of flash memory. (Yes. I mean MBytes here, not MBit)

Looks a lot like a ESP-12E module on the board

If you don't know about CaptiveIntraweb, here is the original video from May 2015, although quite a bit of progress has been made since then.

With the new CaptiveIntraweb  loaded, the module startup now looks like this:
 ---------------------  
 Setting up WiFi AP...  
 Done.  
 ---------------------  
 Flash size is 4096 kBytes.  
 File system:  
  Total : 3360 kBytes  
  Used : 124 kBytes  
  Remain: 3236 kBytes  
 ---------------------  
 Compiling LUA files...  
 No need to compile   dns-liar.lua  
 No need to compile   server.lua  
 Compiling done.  
 ---------------------  
 Send some xxxx Keystrokes now to abort startup.  
 Will launch servers in 5 seconds...  
 > ---------------------  
 Starting HTTP Server  
 HTTP Server listening. Free Heap:    13080  
 Starting DNS Server  
 DNS Server listening. Free Heap:    10008  
 ---------------------  

Tuesday, 8 September 2015

Arduino and ESP8266 - part 2 - The web thermometer

In the previous video, I've shown various options to connect an ESP8266 module to an Arduino board. Now it is time to turn that into something (sortof) userful.

Watch this video to see how I build a web connected thermometer.



The program is a bit particular in that it wants AT-firmware 0.9.2.2 to run stable. I have tried the latest AT firmware but had no luck and didn't invenstigate any further. It's default baud rate of 9k6 is slow enough for a SoftwareSerial connection.

There are no surprises in the schematic diagram:



Here ist the code:


 /* ====== ESP8266 Web Thermometer Demo ======  
  * Print out temperature from TMP36 sensor  
  * on analog-in 3  
  * (Modified Aug 28, 2015 ARe)  
  * ==========================  
  *  
  * Change SSID and PASS to match your WiFi settings.  
  * The IP address is displayed to serial upon successful connection.  
  *  
  * modified by Andy Reischle (www.AReResearch.net)  
  * based on:  
  * Ray Wang @ Rayshobby LLC  
  * http://rayshobby.net/?p=9734  
  */  
 #define SSID "MYWIFI"   // change this to match your WiFi SSID  
 #define PASS "sorrywonttell" // change this to match your WiFi password  
 #define PORT "80"      // using port 80 by default  
 char buffer[BUFFER_SIZE];  
 // using Software Serial for connection to ESP  
 // Use the definitions below  
 #include <SoftwareSerial.h>  
 SoftwareSerial esp(10,11); // used pins 10, 11 for software serial   
 #define dbg Serial  
 // By default we are looking for OK\r\n  
 char OKrn[] = "OK\r\n";  
 byte wait_for_esp_response(int timeout, char* term=OKrn) {  
  unsigned long t=millis();  
  bool found=false;  
  int i=0;  
  int len=strlen(term);  
  // wait for at most timeout milliseconds  
  // or if OK\r\n is found  
  while(millis()<t+timeout) {  
   if(esp.available()) {  
    buffer[i++]=esp.read();  
    if(i>=len) {  
     if(strncmp(buffer+i-len, term, len)==0) {  
      found=true;  
      break;  
     }  
    }  
   }  
  }  
  buffer[i]=0;  
  dbg.print(buffer);  
  return found;  
 }  
 void setup() {  
  // assume esp8266 operates at 9600 baud rate  
  // change if necessary to match your modules' baud rate  
  esp.begin(9600);  
  dbg.begin(9600);  
  dbg.println("begin.");  
  setupWiFi();  
  // print device IP address  
  dbg.print("device ip addr:");  
  esp.println("AT+CIFSR");  
  wait_for_esp_response(1000);  
 }  
 bool read_till_eol() {  
  static int i=0;  
  if(esp.available()) {  
   buffer[i++]=esp.read();  
   if(i==BUFFER_SIZE) i=0;  
   if(i>1 && buffer[i-2]==13 && buffer[i-1]==10) {  
    buffer[i]=0;  
    i=0;  
    dbg.print(buffer);  
    return true;  
   }  
  }  
  return false;  
 }  
 void loop() {  
  int ch_id, packet_len;  
  char *pb;   
  if(read_till_eol()) {  
   if(strncmp(buffer, "+IPD,", 5)==0) {  
    // request: +IPD,ch,len:data  
    sscanf(buffer+5, "%d,%d", &ch_id, &packet_len);  
    if (packet_len > 0) {  
     // read serial until packet_len character received  
     // start from :  
     pb = buffer+5;  
     while(*pb!=':') pb++;  
     pb++;  
     if (strncmp(pb, "GET /", 5) == 0) {  
      wait_for_esp_response(1000);  
      dbg.println("-> serve homepage");  
      serve_homepage(ch_id);  
     }  
    }  
   }  
  }  
 }  
 void serve_homepage(int ch_id) {  
  String header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\nRefresh: 5\r\n";  
  String content="";  
 // read in TMP36 at Analog 3 port  
  int reading = analogRead(3);  
  float voltage = reading * 5.0;  
  voltage /= 1024.0;   
  float temperatureC = (voltage - 0.5) * 100 ;  
  Serial.print(temperatureC); Serial.println(" degrees C");  
  content += "Temperature is ";  
  content += temperatureC;  
  content += "C <br />\n";  
  header += "Content-Length:";  
  header += (int)(content.length());  
  header += "\r\n\r\n";  
  esp.print("AT+CIPSEND=");  
  esp.print(ch_id);  
  esp.print(",");  
  esp.println(header.length()+content.length());  
  if(wait_for_esp_response(2000, "> ")) {  
   esp.print(header);  
   esp.print(content);  
  } else {  
   esp.print("AT+CIPCLOSE=");  
   esp.println(ch_id);  
  }  
 }  
 void setupWiFi() {  
  // try empty AT command  
  esp.println("AT");  
  wait_for_esp_response(2000);  
  // set mode 1 (client)  
  esp.println("AT+CWMODE=1");  
  wait_for_esp_response(2000);   
  // reset WiFi module  
 /*  
  esp.print("AT+RST\r\n");  
  wait_for_esp_response(3000);  
  delay(5000);  
  */  
  // join AP  
  esp.print("AT+CWJAP=\"");  
  esp.print(SSID);  
  esp.print("\",\"");  
  esp.print(PASS);  
  esp.println("\"");  
  // this may take a while, so wait for 5 seconds  
  wait_for_esp_response(5000);  
  esp.println("AT+CIPSTO=30");   
  wait_for_esp_response(1000);  
  // start server  
  esp.println("AT+CIPMUX=1");  
  wait_for_esp_response(1000);  
  esp.print("AT+CIPSERVER=1,"); // turn on TCP service  
  esp.println(PORT);  
  wait_for_esp_response(1000);  
 }  

I have made two modifications to the code:

  • To keep the Arduino's original debugging facility, I kept the hardware UART for that purpose and defined a "software serial" port on pins 10 and 11. The suggested firmware version defaults to 9600 baud, so speed is not an issue.
  • The other change is that I nicked a few lines of code from Adafruit to replace the original output

Thursday, 2 July 2015

Second (successful) attempt to route IP packets with ESP8266

Now that one was a piece of cake:
As suspected /app/include/lwipopts.h overruled the file I modified previously.

So with a 
#define IP_FORWARD 1
in there, the the module started forwarding packets

Ping connectivity table
PING 192.168.1.50 192.168.1.75 192.168.4.1 192.168.4.3
192.168.1.50 n/a n/a n/a YES
192.168.1.75 YES n/a n/a n/a
192.168.4.1 YES n/a n/a YES
192.168.4.3 YES n/a YES n/a



So I now could ping the Laptop (ouch: forgot to disable the firewall first) from the mobile device and vice versa.
Here is the caveat:
  • I need a static route from the Laptop to the "StationMode" interface of the ESP8266. The cooler way to do that is on the default router.
Up to now this is an easy one for the ESP module. Both subnets are directly connected, so the routing decision is not really that hard. With very little tweaking, this could already be used  as an extremely simple range extender working at layer 3.
To daisy-chain / mesh more of these, I need to supply routing information to LWIP. It is not too obvious where to do that in the code or what it takes to add commands to manipulate routes through LUA.

Wednesday, 1 July 2015

First (failed) attempt to route IP packets with ESP8266

In every other comment on my previous projects, someone brought up the topic of mesh networking with ESP8266 modules. Gooooogling the topic, I couldn't find a project that had made into a stable release. (Drop me a line in the comments if you found/have one)

Taking things nice & slow, I started with the following setup:




Ping connectivity table
PING 192.168.1.50 192.168.1.75 192.168.4.1 192.168.4.3
192.168.1.50 n/a n/a n/a n/a
192.168.1.75 YES n/a n/a n/a
192.168.4.1 YES n/a n/a YES
192.168.4.3 NO n/a n/a n/a

So as expected, the ESP8266/NodeMCU does not forward IP packets. I had a peek at the NodeMCU sources and made the following change to /app/include/lwip/opt.h:

 /**  
  * IP_FORWARD==1: Enables the ability to forward IP packets across network  
  * interfaces. If you are going to run lwIP on a device with only one network  
  * interface, define this to 0.  
  */  
 #ifndef IP_FORWARD  
 // #define IP_FORWARD           0  
 #define IP_FORWARD           1  
 #endif  

I rebuilt the firmware, flashed it to a ESP Module and:

FAILED. No changes to the module's routing behaviour.

If anyone is more familiar with the code, don't hesitate to leave a comment.


Next Steps:

  • Get packet forwarding to work
  • Routes? What routes? (There is a hook for those in later versions of LWIP. Backporting possible?)



PS:
Another search of the source code shows that there is another place that sets IP_FORWARD: /app/include/lwipopts.h - I'll try that next.







Wednesday, 17 June 2015

Installed new build environment for ESP8266 firmware

It seems about time to refresh the firmware for my CaptiveIntraweb portal. There are three issues I hope to address:

  • I couldn't get my i2c OLED to work with the February build of NodeMCU. My firmware is based on that.
  • A user from Chicago reported problems with Olimex modules. I ordered a pair of them because they'd make ideal platforms for throwies with their 2MByte (as opposed to 512kByte) i2c flash.
  • It all seems to be open source now, so I can redistribute the firmware freely. Although it looks like a blend of quite a few flavours of open source licenses.

What I have done up to now:

  • Installed the espressif ubuntu image from here
  • Installed the build environment with pfalcon's excellent esp-open-sdk
  • Downloaded NodeMCU sources from the master branch

I have made the following changes to the code:

  • In ./app/include/user_config.h around line 50, comment in #define LUA_NUMBER_INTEGRAL to reduce overall memory requirements
  • Changed line 10 in ./app/include/user_version.h to #define NODE_VERSION    "NodeMCU 0.9.5/AReResearch", so I can see I am on my homebaked version when the module boots up
  • In ./app/include/lwip/app/dhcpserver.h add #define USE_DNS somewhere (around line 54)


I have not changed the Buffer size:

Line 545 in ./app/lua/luaconf.h is currenlty left at:
#define LUAL_BUFFERSIZE         ((BUFSIZ)*4)

This had caused problems with the dns-liar.lua script in the past. If it still does, I will change that.
With regards to the 2MByte Olimex modules, the code defaults to "auto" for the flash size. But I'll have to wait for the modules to arrive to check that out.

Next step: try it on an esp-01 module
That worked ok.
Next step: try it on an Olimex MOD-WIFI-ESP8266-DEV

Edit: 20150622

Wednesday, 20 May 2015

Mini WiFi-Throwie with CR123A

Last night I finally found the time to fire up the soldering iron and try the CaptiveIntraweb with a CR123A cell.
As expected, this worked without any issues. From the data sheet, I expect a run time of approx. one day, although I haven't tried that yet.
Again the discharge rate of  around 70mA ist significantly higher than the 20mA mentioned in the data sheet. From this page it looks like the Panasonic cell is the best choice for high discharge currents.
A bit of tape (Kapton tape should be the ideal choice) to told it together and that's it.
Just don't forget to recover the throwie after the battery is drained and recycle it properly.

I will have the video on my Youtube Channel as soon as I have it ready.