The Arduino Command Line Interface (CLI)

The Arduino folks just came out with the Arduino Command Line Interface (CLI) :

A while back, I wrote about the  Arduino-Builder which is what the Arduino IDE invokes behind the scenes to compile the sketch. (That post is here.) Now, with the Arduino Command Line Interface, everything that can be done from the IDE can now be done on the command line.

The Arduino Command Line Interface can be downloaded from here. By the way, it is also written in the Go language.

I downloaded the file for my system (Linux 64 bit):

curl http://downloads.arduino.cc/arduino-cli/arduino-cli-0.2.0-alpha.preview-linux64.tar.bz2 -o arduino-cli.bz2

Extracted it:

tar -xvf arduino-cli.bz2

and moved it to an easier name:

mv arduino-cli-linux64 arduino-cli

I tried it out and got it working OK for the BluePill but had to make some changes to the boards.txt configuration file.

Here is how I got it working for both the Arduino Uno and  the BluePill (STM32F103C8T6):

Create these directories first (the are needed and not created if they don’t exist):

mkdir $HOME/Arduino
mkdir $HOME/.arduino15

Create a basic config file since it will need to be modified to add the BluePill info:

./arduino-cli config dump >.cli-config.yml

Edit the .cli-config.yml and add these lines at the end:

board_manager:
additional_urls:
- http://dan.drown.org/stm32duino/package_STM32duino_index.json

Next, create a basic test sketch (and optionally edit it):

./arduino-cli sketch new tests

Next, create the index json files via:

./arduino-cli core update-index

Optionally look and see what boards are available to install:

./arduino-cli core search stm

Then install the stm32duino files for the BluePill:

./arduino-cli core install stm32duino:STM32F1

Optionally install the core files for the Arduino Uno (etc.):

./arduino-cli core install arduino:avr

You can see a list of the installed boards via:

./arduino-cli board listall

If you try and compile the test sketch now via:

./arduino-cli compile -v -b stm32duino:STM32F1:genericSTM32F103C $HOME/Arduino/test

You will see a bunch of errors, so you will need to edit the boards.txt file:

vi $HOME/.arduino15/packages/stm32duino/hardware/STM32F1/2018.7.2/boards.txt

To eliminate the errors, change some of the genericSTM32F103C lines with  the string genericSTM32F103C.menu….build. in them to just genericSTM32F103C.build. lines. Make sure there are no duplicates. (The .menu. lines are for selections in the Arduino IDE.)

Once all the correct options are fixed in the boards.txt config file, the sketch should compile fine via:

./arduino-cli compile -v -b stm32duino:STM32F1:genericSTM32F103C $HOME/Arduino/test

To see if the arduino-cli recognizes your attached BluePill (or other Arduino), run:

./arduino-cli board list

To upload the sketch:

./arduino-cli upload -p /dev/ttyACM0 --fqbn stm32duino:STM32F1:genericSTM32F103C $HOME/Arduino/test

Libraries can be found with a search:

./arduino-cli lib search ethernet

And installed via:

./arduino-cli lib install "UIPEthernet"

……………………….

At no time did my fingers leave the keyboard… I like the cli !

STM32F103C8T6 DIY Programming Cradle

I recently created an STM32F103C8T6 DIY programming cradle. Here is what it looks like:

STM32F103C8T6 DIY Programming CradleIt comes in handy when you need to program a bunch of STM32F103C8T6s. (Note: I made a similar ‘cradle’ for programming bunches of ESP8266s’.  Read about that here.)

A standard USB to serial converter is attached to the 6 pin header shown on the right side. Here are some pictures. When the STM32F103C8T6 is ‘plugged in’:

STM32F103C8T6 DIY Programming CradleWhen it is attached to a USB / Serial converter:

STM32F103C8T6 DIY Programming Cradle

This Fritzing graphic shows how the connections are made from the USB to serial converter to the STM32F103C8T6:

STM32F103C8T6 Serial Connections

Download the Fritzing source here.

Push a Button and Play a Video

To motivate and engage sales people, have a big red button to push when a sale is made. They push a button and play a video. Something fun! Add flashing lights for extra effect!

Here are two different means to accomplish this. One solution involves an Arduino UNO serially attached to a PC running  the Windows OS. The other solution is just a Raspberry Pi running OSMC. OSMC basically turns a Raspberry Pi into an entertainment system.

A large screen can be attached to either the Raspberry Pi or a PC to display the video.  A Raspberry PI has GPIO pins which allow it to connect to the outside world. This makes it a standalone solution for this application. A normal PC has no such connections, so that is why something like the Arduino is needed.

The Arduino UNO connects to the PC via a USB to serial cable. This is how the Arduino can be wired to a button:

Push a button and play a videoDownload the fritzing source HERE.  Note: The wiring is basically the same for a larger big red button which would be easier to push (and more fun!)

Notice how the standard 4 pin push button is wired:

4 Pin ButtonWhen the button is pushed,  all four pins are connected. This is the Arduino sketch for monitoring the button push:

/*
Arduino UNO Sketch for Monitoring a button push
When a button is pushed, a 'message' is sent out
the serial port.

From: earl@microcontrollerelectronics.com
*/

const int buttonPin = 2;
const int ledPin    = 13;
int    buttonState  = 0;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
}

void loop() {
  buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH) {
    digitalWrite(ledPin, HIGH);
    Serial.println("Button Press");
    delay(2000);
  } else {
    digitalWrite(ledPin, LOW);
  }
}

The  Arduino communicates serially to a Python script. The Python script, when notified of a button push, will start the video player to play the video. Here is the script:

# Python Script to monitor the serial port where the
# Arduino is attached. If something is sent, it means
# a button was pushed and a video is launched via VLC
#
# From: earl@microcontrollerelectronics.com
#

import serial, os, time, serial.tools.list_ports

cmd = '"C:\\Program Files\\VideoLAN\\VLC\\vlc.exe" -f --play-and-exit'

for p in serial.tools.list_ports.comports():
  sp = str(p)
  if (sp.find('Arduino') != -1):
    flds = sp.split()
    port = flds[0]
    print port

ser = serial.Serial(port, 9600, timeout=0)

while 1:
  try:
    line = ser.readline()
    if (line):
      print cmd
      os.system(cmd)
  except:
    pass
  time.sleep(1)

Since that python script was designed to run on a Windows PC, VLC and Python need to be installed for it to work. The command to run VLC will need to be modified to the URL or file for the specific video to play.

The alternative solution to “Push a Button and Play a Video” is just a Raspberry Pi.  After installing the OSMC distribution,  use these commands to install the prerequisite software :

apt-get dist-upgrade
apt-get install python-pip
apt-get install gcc
apt-get install python-dev

pip install setuptools
pip install wheel
pip install RPi.GPIO
pip install xbmc-json

The prerequisite software is needed to allow a Python script to access the GPIO pins and interface with OSMC.  Next, wire the Raspberry Pi  to the button as show here:

Push a Button and Play a Video

Download the fritzing source HERE. A Python script (running on the Raspberry Pi) is used to monitor for the button push and run the video:

#!/usr/bin/python
#From: earl@microcontrollerelectronics.com 

import RPi.GPIO as GPIO
import time,os
from  xbmcjson import XBMC, PLAYER_VIDEO

def my_callback(channel):
  xbmc = XBMC("http://localhost/jsonrpc",'osmc','osmc')
#  print xbmc.JSONRPC.Ping()
  xbmc.Player.Stop([PLAYER_VIDEO])
  xbmc.Player.Open(item={"file":"/home/osmc/Movies/motivational.mp4"})
#  video_id = ""
#  xbmc.Player.Open(item={"file":"plugin://plugin.video.youtube/?action=play_video&videoid=" + video_id})
  xbmc.Input.Back()
  xbmc.Input.Back()
  xbmc.GUI.ActivateWindow("videos")
# xbmc.GUI.ActivateWindow({"window":"videos"})
# xbmc.GUI.ActivateWindow(window="videos")

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(18,GPIO.OUT)
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(17, GPIO.FALLING, callback=my_callback, bouncetime=200)

while (1):
#  if GPIO.input(17): print('Input was HIGH')
#  else:              print('Input was LOW')

  print "LED on"
  GPIO.output(18,GPIO.HIGH)
  time.sleep(1)
  print "LED off"
  GPIO.output(18,GPIO.LOW)
  time.sleep(1)

The Raspberry Pi running OSMC uses the xbmc_json python library to control  the native OSMC video player. The command to open the player and run the video will need to be modified to the URL or file for the specific video to play.

Which ever solution you choose, push a button and play a video! Fun!

Pulse Width Modulation

MicroControllers are digital devices and usually do not have built in digital to analog converters (or DACs) to output an analog signal. Some, however, do have analog input. For digital output, the voltage is one of two values. Output voltage is either 0 volts or near equal to the voltage source (‘off’ or ‘on’). Using a technique called pulse width modulation (or PWM), a microcontroller can create a ‘simulated’ analog output voltage.

Pulse width modulation turns the output on and off, each with set times at high frequencies to achieve an analog voltage. If you are unfamiliar with pulse width modulation, there are many tutorials on the internet. (Click HERE to search Google for PWM. An excellent one for the Arduino is HERE.)

Here is a short video of pulse width modulation on a PIC12F675 captured on an MSO-19 oscilloscope:

 

As can be seen in the video, the duty cycle (ratio of ‘on’ versus ‘off’)  goes back and forth from 100% to 0%.

Here is the MPLAB XC8 C source code for creating that PWM signal on a PIC12F675:

// From: earl@microcontrollerelectronics.com

//                      _________   __________
//                     |         \_/          |
//                   __|      _               |__
//                  |  | Vdd (_)          Vss |  |
//        Vdd`  +5V |1 |                      | 8|         VSS
//                  |__|                      |__|
//                     |                      |
//                   __|                      |__
//                  |  |              ICSPDAT |  |
// GP5 / T1CKI      |2 |                      | 7| GP0 / AN0 / CIN+
//   CLKIN          |__|                      |__| ICSPDAT / ULPWU
//                     |                      |
//                   __|                      |__
//                  |  |              ICSPCLK |  |
// T1G / AN3 / GP4  |3 |                      | 6| GP1 / AN1 / CIN- /Vref
//  OSC2 / CLKOUT   |__|                      |__| ICSPCLK
//                     |                      |
//                   __| ____                 |__
//                  |  | MCLR/Vpp             |  |
// GP3 / MCLR / VPP |4 |                      | 5| GP2 / AN2 / T0CKI / INT
//                  |__|   PIC12F675 or       |__| COUT / CCP1
//                     |  PIC12F629 no Analog |
//                     |______________________|
//

/*

       PIC TIMER0 Calculator

Clock Source in Mhz                   4 Mhz
Fosc                                  4000000.0 Hz
Fosc / 4                              1000000.0 Hz
Time Period                           1e-06 sec
Prescaler                             32
Timer0 Interrupt Period               0.008192 sec
Period of Frequency Input To Timer0   3.2e-05 sec
Period of Time for each Timer0 Count  0.008192 sec
                
*/

//  xc8 --chip=12F675 main.c
//  xc8 --chip=12F629 main.c

// CONFIG
#pragma config FOSC  = INTRCIO  // Oscillator Selection bits
                                // (INTOSC oscillator: I/O function on GP4/OSC2/CLKOUT pin, I/O function on GP5/OSC1/CLKIN)
#pragma config WDTE  = OFF      // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-Up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = OFF      // GP3/MCLR pin function select
                                // (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config CP    = OFF      // Code Protection bit (Program Memory code protection is disabled)
#pragma config CPD   = OFF      // Data Code Protection bit (Data memory code protection is disabled)

#include <xc.h>

#define _XTAL_FREQ 4000000
#define usdelay  10
int PWM = 254;

void interrupt isr(void) {
  if (T0IF == 1) { 
    if (GP4) TMR0 = (unsigned int)PWM;
    else     TMR0 = 255 - (unsigned int)PWM;
    #asm
      movlw 0b01110111         // mask
      xorwf GPIO,f             // toggle GPIO
      bcf   INTCON,2           // clear T0IF
    #endasm
//  T0IF = 0;
  }
} 

void main(void) {
  OSCCAL     = __osccal_val(); // Load Oscillator Calibration
  VRCON      = 0x00;           // Disable voltage reference
  CMCON      = 0b00000111;     // Disable comparator
  ANSEL      = 0x00;           // A/D disabled
  GPIO       = 0;              // Clear GPIO
  TRISIO     = 0b00001000;     // Set all GPIO (except GPIO3 as outputs
  WPU        = 0;              // Disable all weak pull up
  INTCON     = 0b10100000;     // Global Interrupt Enabled and TMR0 Overflow Interrupt Enabled
  TMR0       = 0         ;     // Preload timer register
  OPTION_REG = 0b00000100;     // Start Timer0 and set Prescaler to 1:32

  int d = 1;
  while (1) {
    PWM += d;
    if(PWM == 255) d = -1;
    __delay_ms(usdelay);
    if(PWM ==   0) d = 1;
  }
}

The original code was generated with my PIC Timer0 Code Generator and Calculator for a PIC12F675:

Pulse Width Modulation on a PIC12F675

and then modified to vary the duty cycle. An MSO-19 oscilloscope was used to capture the  PWM signal. If LEDs were placed on the pins of the PIC12F675, they would be seen gradually going from bright to dim and back again to bright.

 

Using pulse width modulation, an output voltage can be produced anywhere between zero volts and the source voltage. Its simply a matter of math!  For example, if  the source voltage is 5 volts,  to create a 2.5 volt output,  the output pin would be ‘on’ half of the time and ‘off’ half of the time.   (5  ÷ 2 = 2.5)  Of course the frequency of the switching cycle has to be significant.

PIC Timer0 Code Generator and Calculator

I needed an easy way to calculate Microchip PIC timer 0 values, so I created the PIC Timer0 Code Generator and Calculator.  If you are not familiar with PIC timers, there is a handy tutorial HERE.

The calculator part, is a spreadsheet which calculates the timings and timer 0 frequency counter needed, given the  required timer (delay). The code generator part, is a Python script which generates the C source code for the PIC microcontroller XC8 compiler.

Here is what the spreadsheet looks like:

                    PIC TIMER0 Calculator					
					
Clock Source in Mhz                     4 Mhz			
Fosc                                    4000000	Hz			
Fosc / 4                                1000000	Hz			
Time Period                             1E-06    sec			
Prescaler                               32
Timer0 Interrupt Period                 0.008192 sec
Period of Frequency Input To Timer0     3.2E-05  sec
Period of Time for each Timer0 Count    0.008192 sec
					
Preload (TMR0 value)                    6
Delay (Timer0 Period with Preload)      0.008	sec		0.008	sec
					
Required Timer                          1	sec
Number of Interrupts                    125	

Basically, the required timer (i.e. delay) in seconds is specified along with the clock source in Mhz and the prescaler. Optionally a value can be specified for the TMR0 preload to adjust the timing.  All the other values are automatically calculated.  From the example, a 1 second delay is specified with a clock of 4Mhz,  prescaler is 32 and preload is 6.  The calculations show that 125 timer 0 interrupts will take 1 second.

The spreadsheet calculator can be downloaded from HERE.

In these examples, I use the PIC12F675 programmed with a PicKit2 programmer:

PIC Timer0 Code Generator and CalculatorPIC12F675

 

 

 

 

However, even though I use a PIC12F675 for the PIC Timer0 Code Generator and Calculator, it can easily be adapted to any other PIC microcontroller which needs timer 0 calculations. Actually, the spreadsheet part works works for any PIC since it does not generate code. The Python script, which generates code, would only need minor changes for other PIC microcontrollers.

Here is the Python script (gentimer0.py) which generates the C code:

#!/bin/env python
#from earl@microelectronics.com

import os,sys

if (len(sys.argv) < 4):
  print("Syntax: %s clock prescaler TMR0 period") % (sys.argv[0]) 
  sys.exit()

Clock     = int(sys.argv[1])
Prescaler = int(sys.argv[2])
TMR0      = int(sys.argv[3])
Period    = int(sys.argv[4])

print("")
print("//                      _________   __________")
print("//                     |         \\_/          |")
print("//                   __|      _               |__")
print("//                  |  | Vdd (_)          Vss |  |")
print("//        Vdd`  +5V |1 |                      | 8| 	VSS")
print("//                  |__|                      |__|")
print("//                     |                      |")
print("//                   __|                      |__")
print("//                  |  |              ICSPDAT |  |")
print("// GP5 / T1CKI      |2 |                      | 7| GP0 / AN0 / CIN+")
print("//   CLKIN          |__|                      |__| ICSPDAT / ULPWU")
print("//                     |                      |")
print("//                   __|                      |__")
print("//                  |  |              ICSPCLK |  |")
print("// T1G / AN3 / GP4  |3 |                      | 6| GP1 / AN1 / CIN- /Vref")
print("//  OSC2 / CLKOUT   |__|                      |__| ICSPCLK")
print("//                     |                      |")
print("//                   __| ____                 |__")
print("//                  |  | MCLR/Vpp             |  |")
print("// GP3 / MCLR / VPP |4 |                      | 5| GP2 / AN2 / T0CKI / INT")
print("//                  |__|                      |__| COUT / CCP1")
print("//                     |      PIC12F675       |")
print("//                     |______________________|")
print("//")
print("/*")
print("")
print("       PIC TIMER0 Calculator")
print("")

Fosc       = Clock * 1000000.0
Fosc4      = Fosc / 4.0
Cycle      = 1.0 / Fosc4
IntPeriod  = 1.0 / ( Fosc4 / Prescaler / 256)
FreqPeriod = 1.0 / ( Fosc4 / Prescaler)
TCount     = 256 * FreqPeriod
Delay      = ((256 - TMR0) * (Prescaler * 4)) / Fosc
NumInts    = Period / Delay

print("Clock Source in Mhz                   %s Mhz") % (Clock)
print("Fosc                                  %s Hz")  % (Fosc)
print("Fosc / 4                              %s Hz")  % (Fosc4)
print("Time Period                           %s sec") % (Cycle)
print("Prescaler                             %s")     % (Prescaler)

print("Timer0 Interrupt Period               %s sec") % (IntPeriod)
print("Period of Frequency Input To Timer0   %s sec") % (FreqPeriod)
print("Period of Time for each Timer0 Count  %s sec") % (TCount)
print("		")
print("Preload (TMR0 value)                  %s")     % (TMR0)
print("Delay                                 %s	sec") % (Delay)
print("		")
print("Required Timer                        %s sec") % (Period)
print("Number of Interrupts                  %s")     % (NumInts)
print("")
print("*/")
print("")
print("//  xc8 --chip=12F675 main.c")
print("")
print("// CONFIG")
print("#pragma config FOSC  = INTRCIO  // Oscillator Selection bits")
print("                                // (INTOSC oscillator: I/O function on GP4/OSC2/CLKOUT pin, I/O function on GP5/OSC1/CLKIN)")
print("#pragma config WDTE  = OFF      // Watchdog Timer Enable bit (WDT disabled)")
print("#pragma config PWRTE = OFF      // Power-Up Timer Enable bit (PWRT disabled)")
print("#pragma config MCLRE = OFF      // GP3/MCLR pin function select")
print("                                // (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD)")
print("#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)")
print("#pragma config CP    = OFF      // Code Protection bit (Program Memory code protection is disabled)")
print("#pragma config CPD   = OFF      // Data Code Protection bit (Data memory code protection is disabled)")
print("")
print("#include <xc.h>")
print("")
print("#define _XTAL_FREQ %s") % (int(Fosc))
print("")
print("#define Timer0_Counter %s //Timer0 interrupt count") % (int(NumInts)) 
print("unsigned int cycles = 0;")
print("")
print("void interrupt isr(void) {")
print("//If T0IF: TMR0 Overflow Interrupt Flag bit equal 1, service timer interrupt")
print("  if (T0IF == 1) { ")
print("    cycles++;")
print("    TMR0 = %s;  // Preload timer register") % (TMR0)
print("    T0IF = 0;")
print("  }")
print("} ")
print("")
print("void main(void) {")
print("  OSCCAL     = __osccal_val(); // Load Oscillator Calibration")
print("  VRCON      = 0x00;           // Disable voltage reference")
print("  CMCON      = 0b00000111;     // Disable comparator")
print("  ANSEL      = 0x00;           // A/D disabled")
print("  GPIO       = 0;              // Clear GPIO")
print("  TRISIO     = 0b00001111;     // Set GPIO4 AND GPIO5 as outputs. Set GPIO0, GPIO2, GPI01 and GPIO3 as inputs")
print("  WPU        = 0;              // Disable all weak pull up")
print("  INTCON     = 0b10100000;     // Global Interrupt Enabled and TMR0 Overflow Interrupt Enabled")
print("  TMR0       = % -8s;       // Preload timer register") % (TMR0)

if (Prescaler == 2):   print("  OPTION_REG = 0b00000000;     // Start Timer0 and set Prescaler to 1:2")
if (Prescaler == 4):   print("  OPTION_REG = 0b00000001;     // Start Timer0 and set Prescaler to 1:4")
if (Prescaler == 8):   print("  OPTION_REG = 0b00000010;     // Start Timer0 and set Prescaler to 1:8")
if (Prescaler == 16):  print("  OPTION_REG = 0b00000011;     // Start Timer0 and set Prescaler to 1:16")
if (Prescaler == 32):  print("  OPTION_REG = 0b00000100;     // Start Timer0 and set Prescaler to 1:32")
if (Prescaler == 64):  print("  OPTION_REG = 0b00000101;     // Start Timer0 and set Prescaler to 1:64")
if (Prescaler == 128): print("  OPTION_REG = 0b00000110;     // Start Timer0 and set Prescaler to 1:128")
if (Prescaler == 256): print("  OPTION_REG = 0b00000111;     // Start Timer0 and set Prescaler to 1:256")

print("")
print("  GP4 = 1;")
print("  while (1) {")
print("    if (cycles >= Timer0_Counter) {")
print("      cycles = 0;")
print("// Add code here - timer expired")
print("      GP4 ^= 1; // Toggle LED on GP4")
print("")
print("    }")
print("// Main Code goes here")
print("")
print("  }")
print("}")

Here is the command to run it:

./gentimer0.py 4 32 6 1 >timer.c

and here is the resultant output:

//                      _________   __________
//                     |         \_/          |
//                   __|      _               |__
//                  |  | Vdd (_)          Vss |  |
//        Vdd`  +5V |1 |                      | 8| 	VSS
//                  |__|                      |__|
//                     |                      |
//                   __|                      |__
//                  |  |              ICSPDAT |  |
// GP5 / T1CKI      |2 |                      | 7| GP0 / AN0 / CIN+
//   CLKIN          |__|                      |__| ICSPDAT / ULPWU
//                     |                      |
//                   __|                      |__
//                  |  |              ICSPCLK |  |
// T1G / AN3 / GP4  |3 |                      | 6| GP1 / AN1 / CIN- /Vref
//  OSC2 / CLKOUT   |__|                      |__| ICSPCLK
//                     |                      |
//                   __| ____                 |__
//                  |  | MCLR/Vpp             |  |
// GP3 / MCLR / VPP |4 |                      | 5| GP2 / AN2 / T0CKI / INT
//                  |__|                      |__| COUT / CCP1
//                     |      PIC12F675       |
//                     |______________________|
//
/*

       PIC TIMER0 Calculator

Clock Source in Mhz                   4 Mhz
Fosc                                  4000000.0 Hz
Fosc / 4                              1000000.0 Hz
Time Period                           1e-06 sec
Prescaler                             32
Timer0 Interrupt Period               0.008192 sec
Period of Frequency Input To Timer0   3.2e-05 sec
Period of Time for each Timer0 Count  0.008192 sec
		
Preload (TMR0 value)                  6
Delay                                 0.008	sec
		
Required Timer                        1 sec
Number of Interrupts                  125.0

*/

//  xc8 --chip=12F675 main.c

// CONFIG
#pragma config FOSC  = INTRCIO  // Oscillator Selection bits
                                // (INTOSC oscillator: I/O function on GP4/OSC2/CLKOUT pin, I/O function on GP5/OSC1/CLKIN)
#pragma config WDTE  = OFF      // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-Up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = OFF      // GP3/MCLR pin function select
                                // (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config CP    = OFF      // Code Protection bit (Program Memory code protection is disabled)
#pragma config CPD   = OFF      // Data Code Protection bit (Data memory code protection is disabled)

#include <xc.h>

#define _XTAL_FREQ 4000000

#define Timer0_Counter 125 //Timer0 interrupt count
unsigned int cycles = 0;

void interrupt isr(void) {
//If T0IF: TMR0 Overflow Interrupt Flag bit equal 1, service timer interrupt
  if (T0IF == 1) { 
    cycles++;
    TMR0 = 6;  // Preload timer register
    T0IF = 0;
  }
} 

void main(void) {
  OSCCAL     = __osccal_val(); // Load Oscillator Calibration
  VRCON      = 0x00;           // Disable voltage reference
  CMCON      = 0b00000111;     // Disable comparator
  ANSEL      = 0x00;           // A/D disabled
  GPIO       = 0;              // Clear GPIO
  TRISIO     = 0b00001111;     // Set GPIO4 AND GPIO5 as outputs. Set GPIO0, GPIO2, GPI01 and GPIO3 as inputs
  WPU        = 0;              // Disable all weak pull up
  INTCON     = 0b10100000;     // Global Interrupt Enabled and TMR0 Overflow Interrupt Enabled
  TMR0       = 6       ;       // Preload timer register
  OPTION_REG = 0b00000100;     // Start Timer0 and set Prescaler to 1:32

  GP4 = 1;
  while (1) {
    if (cycles >= Timer0_Counter) {
      cycles = 0;
// Add code here - timer expired
      GP4 ^= 1; // Toggle LED on GP4

    }
// Main Code goes here

  }
}

The source is complied (via the Microchip XC8 compiler) with this command:

/opt/microchip/xc8/v1.45/bin/xc8 --chip=12F675 timer.c

Then use the PicKit2 programmer:

PICKit2 Clone Programmerwith the command:

/opt/microchip/pk2cmd-master/pk2cmd/pk2cmd -PPIC12F675 -Ftimer.hex -M -B/opt/microchip/pk2cmd-master/pk2cmd/

to send it to the chip.
PIC Timer0 Code Generator and CalculatorNotice, that /opt/microchip/ is where I have installed he XC8 compiler and the PicKit2 software (pk2cmd).  If you have an LED on GPIO pin4, it will blink every 1 second. Basically with this code generator, one has a starting point template to add to. The comments in the code show where to put code to make it more useful.

Controlling Things Based on Light Intensity

A PhotoCell or LDR (Light Dependent Resistor), used in a circuit, allows controlling things based on light intensity.

PhotoCell

PhotoCell / LDR (Light Dependent Resistor)

Here is a circuit showing how to wire an LDR and demonstrates how controlling things based on light intensity is possible with a little help from a microcontroller.

PIC12F683 with LDR

Controlling Things Based on Light Intensity

The thing being controlled in this circuit is simply an LED which turns On or Off. However, it could just as easily be a relay to control some other device. (Get the Fritzing source HERE for the above circuit graphic).

An analog value of the voltage which varies with light intensity can be read by a microcontroller. In this circuit, a PIC12F683 microcontroller is used. Based on that value, other circuitry can be controlled (i.e. turned On or Off).

Here is the MPLAB X IDE C source code for the PIC12F683 in the circuit shown above.

// From:  earl@microcontrollerelectronics.com
//                      _________   __________
//                     |         \_/          |
//                   __|      _               |__
//                  |  | Vdd (_)          Vss |  |
//        Vdd`  +5V |1 |                      | 8| 	VSS
//                  |__|                      |__|
//                     |                      |
//                   __|                      |__
//                  |  |              ICSPDAT |  |
// GP5 / T1CKI      |2 |                      | 7| GP0 / AN0 / CIN+
//   CLKIN          |__|                      |__| ICSPDAT / ULPWU
//                     |                      |
//                   __|                      |__
//                  |  |              ICSPCLK |  |
// T1G / AN3 / GP4  |3 |                      | 6| GP1 / AN1 / CIN- /Vref
//  OSC2 / CLKOUT   |__|                      |__| ICSPCLK
//                     |                      |
//                   __| ____                 |__
//                  |  | MCLR/Vpp             |  |
// GP3 / MCLR / VPP |4 |                      | 5| GP2 / AN2 / T0CKI / INT
//                  |__|                      |__| COUT / CCP1
//                     |                      |
//                     |______________________|
//
//                          PIC12F683
/*
 
 Vdd  ------ LDR
             |
 Gnd  --10K--|--------- AN1
 
*/

#include <xc.h>

#define _XTAL_FREQ 8000000

#pragma config FOSC  = INTOSCIO
#pragma config WDTE  = OFF        
#pragma config PWRTE = OFF       
#pragma config MCLRE = OFF       
#pragma config CP    = OFF         
#pragma config CPD   = OFF        
#pragma config BOREN = OFF       
#pragma config IESO  = OFF        
#pragma config FCMEN = OFF       

int main() {
  
  OSCCON = 0b00000000;
  OSCCONbits.IRCF= 0b111;  // 8Mhz
//  OSCCONbits.SCS = 0;      // FOSC <2:0> of Config Word
  OSCCONbits.SCS = 1;      // Internal Oscillator 
    
  CMCON0 = 0b00000000;
  CMCON0bits.CM = 0b111;    // Comparator Off

  GPIO   = 0b00000000;      // Init GPIO
  TRISIO = 0b00000000;      // Init TRSIO
  TRISIObits.TRISIO1 = 1;   // AN1 Input
  TRISIObits.TRISIO3 = 1;   // GP3 Input (only)
  
  ANSEL  = 0b00000000;  
//  ANSELbits.ADCS = 0b101;   // FOSC/16
  ANSELbits.ADCS = 0b010;   // FOSC/32
  ANSELbits.ANS1 = 1;       // AN1
  
  ADCON0 = 0b00000000;
  ADCON0bits.ADFM = 1;      // R1ght Justified
  ADCON0bits.VCFG = 0;      // Vdd
  ADCON0bits.CHS  = 0b01;   // AN1
  ADCON0bits.ADON = 1;      // ADC Enable

  while(1) {
    __delay_ms(5);
    ADCON0bits.GO_DONE = 1; // Go
    
    while (ADCON0bits.GO_DONE == 1) {}

    if ( ((ADRESH << 8) + ADRESL) > 512) GP2 = 1;
    else                                 GP2 = 0;

  }
}

The analog value varies from 0 to 1024 with 0 being no voltage and 1024 being max voltage. The code shows turning on the LED if at least 1/2 of the voltage  (1/2 of 1024 = 512) is being passed by the LDR. Since the source voltage is 5V,  1/2 of it would be 2.5 volts.  Depending on the application for this sort of circuit, some experimentation can be done to determine the optimum value for when to turn something On/Off.

The data sheet for the PIC12F683 is HERE.  It is essential reading in order to understand how to write code for the chip. Native C coding (using the MPLAB X IDE) for PIC microcontrollers is a bit harder than the ‘sheltered’ environment of the Arduino IDE.

Yes, you can do this sort of thing with an Arduino (and the code would be easier to develop) but at a higher component cost. The components in this sample circuit are minimal, small and low cost.

How to keep an ENC28J60 Module from Freezing

The ENC28J60 module is good for attaching an Arduino to a LAN or the Internet. The one real problem in doing so is how to keep an ENC28J60 module from freezing. No, not temperature wise ! 🙄  (‘Freezing’ in the sense of locking up, stop working, etc.)

How to keep an ENC28J60 Module from Freezing

ENC28J60 LAN Module

There are many Ethernet  arduino libraries for the ENC28J60 module, however,  I am using the UIPEthernet library from HERE. It is updated from the original Ethernet and follow on UIPEthernet libraries. Documentation on the Ethernet library can be found HERE along with sample code. Note: The UIPEthernet library I am using fully supports all the documented functions from the original library.

There are various ERRATA on the ENC28J60, however, the current versions of the UIPEthernet  library address those issues. This library also supports the STM32F103C8T6. Usually one would think that such ‘lockup’ or ‘freezing’ problems are related to power problems. That problem is not an issue here, it is taken care of by testing with different good power sources and lots of capacitance.

I have the most trouble when I attach an STM32F103C8T6  to the ENC28J60 module.

ARM Cortex M3 STM32F103C8T6

STM32F103C8T6

I seem to have the least problems when I attach an Arduino UNO to it.

Arduino UNO

Arduino UNO

I think that perhaps the extra speed of the STM32F103C8T6 enhances the problem. In the main loop() code, the library function checks on the ENC28J60 and perhaps due to the faster speed of the STM32F103C8T6 there is something going on that affects the ENC28J60.

Another, less know issue, is the need to occasionally call the library function maintain(). If one looks at the documentation, it seems maintain() is only used to renew a DHCP lease. Further research will show that this function call is needed, periodically, if connected to an ENC28J60.  I found that there are fewer ‘freezing’ problems when calling that function in the main loop() code, however, they still occasionally happen.

The best approach to fully solving the issue is to somehow check the status of the ENC28J60 and reset it if it is ‘frozen’. Fortunately, the code for that is available HERE.

This is my sample WEB server code which shows how to keep an ENC28J60 module from freezing by implementing that checking of the ENC28J60.  It also shows the use of the maintain() function and the use of the STM32F103C8T6 watch dog timer. I use the watch dog timer to make sure the mictocontroller running the code to control the ENC28J60 is not somehow locked up (‘frozen’).

Here is the code:

/*
  Web Server
 
 A simple web server that shows the value of the analog input pins.
 or blinks the built in LED (check the comments)

 From: earl@microcontrollerelectronics.com
 Demonstrates checking the ENC28J60 and keeping it from freezing.

ENC28J60  STM32F103
SCK        PA5
SO         PA6
SI         PA7
CS         PA4
RST        PA0

VCC - 5V
GND - GND

--ENC28J60--
CLK  INT
WOL  SO
SI   SCK
CS   RESET
VCC  GND

             +-----------------[USB]-----------------+
  [SS2|PB12] | [31]                            [Gnd] |
 [SCK2|PB13] | [30]                  +---+     [Gnd] |
[MISO2|PB14] | [29]    +-----+       |0 0|     [3V3] |
[MOSI2|PB15] | [28]    |Reset|       |x x|   [Reset] | 
       [PA8] | [27]    +-----+       |1 1|      [ 0] | [PB11|SDA2|RX3] 
   [TX1|PA9] | [26]                  +---+      [ 1] | [PB10|SCL2|TX3]
  [RX1|PA10] | [25]                   ^ ^       [33] | [PB1]
 [USB-|PA11] | [24]            Boot1--+ |       [ 3] | [PB0|A0]
 [USB+|PA12] | [23]            Boot0----+       [ 4] | [PA7|A1|MOSI1]
      [PA15] | [20]                             [ 5] | [PA6|A2|MISO1]
       [PB3] | [19]        +-----------+        [ 6] | [PA5|A3|SCK1]
       [PB4] | [18]        | STM32F103 |        [ 7] | [PA4|A4|SS1]
       [PB5] | [17]        | Blue Pill |        [ 8] | [PA3|A5|RX2]
  [SCL1|PB6] | [16]        +-----------+        [ 9] | [PA2|A6|TX2]
  [SDA1|PB7] | [15]                             [10] | [PA1|A7]
       [PB8] | [32]                             [11] | [PA0|A8]
       [PB9] | [PB9]                            [12] | [PC15]
             | [5V]      +---------------+      [13] | [PC14]
             | [Gnd]     |    ST-Link    |      [14] | [PC13|LED]
             | [3V3]     |3V3 DIO CLK GND|     [Vbat]| 
             +-------------+---+---+---+-------------+
                           |   |   |   |
*/

#include <libmaple/iwdg.h>
#include <UIPEthernet.h>

// ..utility\Enc28J60Network.h file - 
//move readReg() subroutine def from private to public 

#define NET_ENC28J60_EIR          0x1C
#define NET_ENC28J60_ESTAT        0x1D
#define NET_ENC28J60_ECON1        0x1F
#define NET_ENC28J60_EIR_RXERIF   0x01
#define NET_ENC28J60_ESTAT_BUFFER 0x40
#define NET_ENC28J60_ECON1_RXEN   0x04
#define NET_ENC28J60_CHECK_PERIOD 5000UL

#include <SPI.h>
#define iwdg_init_ms(N) iwdg_init(IWDG_PRE_256,((N)/5))

#define pinLED PC13
#define ETH_RS_PIN PA0

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

IPAddress   ipdns(  8,   8,   8, 8);
IPAddress      ip(192, 168,   2, 50);
IPAddress gateway(192, 168,   2, 1);
IPAddress  subnet(255, 255, 255, 0);

EthernetServer server = EthernetServer(80);

String cdata = "";
unsigned long timer;

void setup() {
  Serial.begin(115200);
  pinMode(pinLED, OUTPUT);
  digitalWrite(pinLED,HIGH);
  eth_reset();
  iwdg_init_ms(4000);
  timer = millis();
}

void eth_reset() { 
  pinMode(ETH_RS_PIN, OUTPUT);
  digitalWrite(ETH_RS_PIN, LOW);
  delay(100);
  digitalWrite(ETH_RS_PIN, HIGH);
  pinMode(ETH_RS_PIN, INPUT);
  
  Ethernet.begin(mac, ip, ipdns, gateway, subnet);
  server.begin();
  Serial.print(F("WEB server is at "));
  Serial.println(Ethernet.localIP());

  Serial.print(F("DNS server is at ")); 
  Serial.println(Ethernet.dnsServerIP()); 
}

void loop() {
  iwdg_feed();
  Ethernet.maintain();
   
  if ((millis() - timer) > NET_ENC28J60_CHECK_PERIOD) {    

       // Enc28J60 is Enc28J60Network class that is defined in Enc28J60Network.h
       // readReg() subroutine must be moved from private to public members area in utility\Enc28J60Network.h
       // ENC28J60 ignore all incoming packets if ECON1.RXEN is not set
       uint8_t stateEconRxen = Enc28J60.readReg((uint8_t) NET_ENC28J60_ECON1) & NET_ENC28J60_ECON1_RXEN;
       // ESTAT.BUFFER rised on TX or RX error
       // I think the test of this register is not necessary - EIR.RXERIF state checking may be enough
       uint8_t stateEstatBuffer = Enc28J60.readReg((uint8_t) NET_ENC28J60_ESTAT) & NET_ENC28J60_ESTAT_BUFFER;
       // EIR.RXERIF set on RX error
       uint8_t stateEirRxerif = Enc28J60.readReg((uint8_t) NET_ENC28J60_EIR) & NET_ENC28J60_EIR_RXERIF;
       Serial.println("---REGS---");
       Serial.println(stateEconRxen,HEX);
       Serial.println(stateEstatBuffer,HEX);
       Serial.println(stateEirRxerif,HEX);
       if (!stateEconRxen || (stateEstatBuffer && stateEirRxerif)) {
         Serial.println ("ENC28J60 reinit");
//          Enc28J60.init(netConfig->macAddress);
         eth_reset();
       }
    timer = millis();
  }
  
  EthernetClient client = server.available();
  
  if (client) {    
    Serial.println(F("\n--Client Connected--\n"));
    cdata = "";
    Serial.println(cdata);
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        cdata.concat(c);
        if (cdata.indexOf("\r\n\r\n") > 0) {
          Serial.print(F("Buffer Length: "));
          Serial.println(cdata.length());
          client.println(F("HTTP/1.1 200 OK"));
          client.println(F("Content-Type: text/html"));
          client.println(F("Connection: close")); 
          if(cdata.indexOf("Analog") > 0) client.println(F("Refresh: 5"));
          client.println();
          client.println(F("<!DOCTYPE HTML>"));
          client.println(F("<html>"));
          client.println(F("<body>"));
          client.println(F("<br><br><center><H1>STM32F103C8T6 WEB Server</H1></center>"));

          if(cdata.indexOf("LED") > 0) {
            digitalWrite(pinLED,!digitalRead(pinLED));
            if (digitalRead(pinLED)) client.println(F("<center><br><br><br><br><H1>LED OFF!</H1></center>"));
            else                     client.println(F("<center><br><br><br><br><H1>LED ON!</H1></center>")); 
            client.println(F("</body>"));
            client.println(F("</html>"));
            break;
          }
          
          if(cdata.indexOf("Analog") > 0) {
            client.println(F("<table border=1 cellspacing=4 cellpadding=4>"));
            client.println(F("<tr><th>Analog</th><th>Value</th></tr>"));
            for (int analogChannel = 0; analogChannel < 4; analogChannel++) {
              int sensorReading = analogRead(analogChannel);
              client.print(F("<tr><td>"));
              client.print(analogChannel);
              client.print(F("</td><td>"));
              client.print(sensorReading);
              client.println(F("</td></tr>"));       
            }
            client.println(F("</table>"));
            client.println(F("</body>"));
            client.println(F("</html>"));
            break;
          }         
          
          client.println(F("<center><br><br><br><br><H1>Unknown Command!</H1></center>"));
          client.println(F("</body>"));
          client.println(F("</html>"));
          break;
        }
      }
    }
    client.stop();
    Serial.println(F("\n--Client Disconnected--"));
    timer = millis();
  }  
}

From the ASCII chart in the comments at the top of the code, one can see how the STM32F103C8T6 is wired to the ENC28J60.  One thing to note, is that the RESET of the ENC28J60 is accomplished via its RESET pin rather than by calling the Enc28J60.init() function.  This does a hardware reset and seems to work in all lockup cases.

Switching 12V from a 5V Arduino

I built a circuit to allow switching 12V from a 5V Arduino. Here is the wiring diagram and a picture of the actual circuit:

Switch 12V from 5V ArduinoSwitching 12V from a 5V Arduino

 

 

 

 

Get the Fritzing source HERE.  The side with two connections attaches to the 12V device being switched ON/OFF. The side with three  connectors,  connects to the 12V source and Ground. It also connects to an Arduino PIN to do the ON/OFF switching.

The actual circuit (pictured above) is doubled and can handle switching two 12V devices.  A PC power supply (converted to a ‘bench power supply’) can be used to power the Arduino and the 12V devices,

You can hook up a PC power supply and make a bench power supply with an adapter. That adapter easily attaches to the power supply and breaks out the 12V, Ground, 5V and 3.3V sources. Pictured below is an example of such an adapter:
PC Bench Power Supply AdaptorThe circuit switching 12V from a 5V Arduino  comes in handy for a number of things. I am using it to turn on a 12V Emergency Alarm.  Here are the two types (siren and siren with strobe) that I am using. They both work with 12V and Ground.

Switching 12V from a 5V ArduinoSwitching 12V from a 5V Arduino

 

 

 

 

 

This circuit switching 12V from a 5V Arduino is part of a much larger home/office alarm system project I am working on.

Decoding an ESP8266 Firmware Image

I recently coded a Python script for Decoding an ESP8266 Firmware Image. It’s the first part of my quest to create a Linux tool for creating a single flash image.

More specifically, the script will decode a single binary image or a ‘combined’ image. A combined image (single flash image) can be created with the flash download tool from Expressif. (Exressif is the company which makes the ESP8266.)  However, that download tool only runs on Windows. Here is what the Expressif Flash Tool looks like:

 

ESP8266 Flash Tool on Windows

Notice, the ‘CombineBin’ button. It is used to create a single binary image from the multiple files (‘segments’). In that image you can see seven files (the current SDK 2.1.0 files as of this post date) listed and the addresses to load them . I used the ‘CombineBin’ button and created a single binary image called sdk-2.1.0.bin. (The default file name it creates is called target.bin.)

I run Linux. and the recommend ESP8266 tool for it is esptool.py. The esptool script, however, does not have a option to create a single combined image. It may have that feature as an enhancement at some future time. Hence, my quest, to develop a Linux tool to create a single flash image.

I like to keep my ESP8266 firmware current. To flash the latest SDK, I use this script:

esptool.py --baud 115200 --port /dev/ttyUSB0 write_flash -fm dio -ff 40m -fs detect \
0x00000 /opt/arduino/esp8266/ESP8266_NONOS_SDK-master/bin/boot_v1.7.bin \
0x01000 /opt/arduino/esp8266/ESP8266_NONOS_SDK-master/bin/at/512+512/user1.1024.new.2.bin \
0x7E000 /opt/arduino/esp8266/ESP8266_NONOS_SDK-master/bin/blank.bin \
0x81000 /opt/arduino/esp8266/ESP8266_NONOS_SDK-master/bin/at/512+512/user2.1024.new.2.bin \
0xFB000 /opt/arduino/esp8266/ESP8266_NONOS_SDK-master/bin/blank.bin \
0xFC000 /opt/arduino/esp8266/ESP8266_NONOS_SDK-master/bin/esp_init_data_default_v08.bin \
0xFE000 /opt/arduino/esp8266/ESP8266_NONOS_SDK-master/bin/blank.bin

While that works fine, I think it would be more efficient to create a single combined image to flash. Especially since I have a number of ESP8266 modules to flash. The single image (sdk-2.1.0.bin created from the windows tool) can be flashed as follows:

esptool.py --baud 115200 --port /dev/ttyUSB0 write_flash -fm dio -ff 40m -fs detect 0x00 sdk-2.1.0.bin

To create a tool to combine all the ‘single’ images into one, one needs to know the format of the single images and the format of the ‘combined’ image.

Espressif has a brief document (HERE), describing the firmware image format. However, that document does not fully cover what is need to decode a combined image. Also, it does not cover all of the single image ‘formats’. Esptool.py has an image_info command, however, it only works on single modules. It does not work on combined modules.

In my ‘reverse engineering’ attempt at Decoding an ESP8266 Firmware Image, I created this Python script called esp8266_parse_bin.py. Here is the code:

#!/usr/bin/env python
#
# esp8266_parse_bin.py Version 1.1 1/22/2018
# (C)opyright 2018 Email: earl@microcontrollerelectronics.com
# Parses (decodes) ESP8266 single or combined binary image files
#
#
# Partial document on the ESP8266 Image Format
# https://github.com/espressif/esptool/wiki/Firmware-Image-Format
#
#    0        1   2         3   4-7   8
# 0xE9 segments SPI mem/speed entry  ...
# 0xEA segments SPI mem/speed entry  ...
#
#  --SEGMENTS--
#
#     0-3   4-7   8-n
#  Offset  size   data

import os, sys, string, struct

fsize    = 0
chk      = 0
htype    = ''
filename = sys.argv[1]
f        = open(filename, "rb")
afsize   = os.stat(filename).st_size
baddr    = ""
bsize    = 0
useg     = ""
uaddr    = ""
usize    = 0

def blank_header():
  global baddr,bsize
  print("%-8s %-12s %-12s %-12s %-12s %-12s") % ("","","Size/Hex","Size/Bytes","File/Offset","File/Offset")
  print("%-8s %-12s %-12s %-12s %-12s %-12s") % ("Blank(s)","0xff",'0x'+hex(bsize)[2:].zfill(8),bsize,'0x'+hex(baddr)[2:].zfill(8),baddr)
  baddr = ""
  bsize = 0

def unknown_header():
  global uaddr,usize,useg 
  print("%-8s %-12s %-12s %-12s %-12s %-12s") % ("","","Size/Hex","Size/Bytes","File/Offset","File/Offset")
  print("%-8s %-12s %-12s %-12s %-12s %-12s") % ("Unknown","",'0x'+hex(usize)[2:].zfill(8),usize,'0x'+hex(uaddr)[2:].zfill(8),uaddr)
  for b in bytearray(useg): print(hex(b)),
  print
  uaddr = ""
  useg  = ""
  usize = 0

print("Parsing: %s Size: %s/%s") % (filename,hex(afsize),afsize)

while(1):
  t = f.read(8)
  if not t: break
  l = len(t)
  sh = ord(t[0])
  if (sh != 0xff):
    if (baddr != ""): blank_header()
  if ((sh == 0xea) or (sh == 0xe9) or (sh == 0xff)):
    if (uaddr != ""): unknown_header()
  if l < 8:
    print("Extra Data [no-header] of Length: %s -> ") % (len(t)),
    for b in bytearray(t): print(hex(b)),
    print
    fsize += l
    break
  h = struct.Struct("<BBBBI").unpack(t)
  if (h[0] == 0xea): 
    segments = 1
    htype    = 0xea
  else:
    if (h[0] == 0xe9):
      segments = int(h[1])
      htype    = 0xe9
      chk      = 0 
    else:
      if (h[0] == 0xff):
        if (baddr == ""): baddr = fsize
        bsize += l
        fsize += l
        continue
      else:
        if (uaddr == ""): uaddr = fsize
        useg  += t
        usize += l
        fsize += l
        continue

  fsize += l
  print "Header: ",hex(h[0]),int(h[1]),hex(h[2]),hex(h[3]),hex(h[4])
  print("%-8s %-12s %-12s %-12s %-12s %-12s") % ("Segment","Offset","Size/Hex","Size/Bytes","File/Offset","File/Offset")
  for x in range(0,segments):
    data = f.read(8)
    if not data: break
    s = struct.Struct("<II").unpack(data)
    offset  = s[0]
    size    = s[1]
    print("%-8s %-12s %-12s %-12s %-12s %-12s") % (x+1,'0x'+hex(offset)[2:].zfill(8),'0x'+hex(size)[2:].zfill(8),size,'0x'+hex(fsize)[2:].zfill(8),fsize),
    if (htype != 0xea):
      if (offset > 0x40200000): print(" <-- Offset > 0x40200000 "),
      if (offset < 0x3ffe0000): print(" <-- Offset < 0x3ffe0000 "),
      if (size > 65536):        print(" <-- Size > 65536 "),
    print
    l = int(s[1])
    fsize += 8 + l
    data = f.read(l)
    for b in bytearray(data): chk ^= int(b)

  if (htype == 0xea): continue
  pad  = 16 - (fsize % 16)
  print("Padding: %s bytes -> ") % (pad),
  data = f.read(pad)
  for b in bytearray(data): print(hex(b)),
  print
  chk = chk ^ 0xEF
  print("Calculated Checksum: %s") % (hex(chk)),
  if (ord(data[-1]) == chk): print " [matches]",
  print
  fsize += pad
  continue

if (uaddr != ""): unknown_header()
if (baddr != ""): blank_header()

print("File Size: %s  Calculated: %s") % (afsize,fsize),
if (afsize == fsize): print(" [matches]"),
print

Esp8266_parse_bin.py can be used to decode any of the single modules that comprise the combined sdk-2.1.0.bin image or it can be used to decode the ‘combined’ image.

When it is used to decode the combined sdk-2.1.0.bin image via:

./esp8266_parse_bin.py sdk-2.1.0.bin

Here is what the output looks like:

Parsing: sdk-2.1.0.bin Size: 0xff000/1044480
Header:  0xe9 3 0x0 0x20 0x4010057c
Segment  Offset       Size/Hex     Size/Bytes   File/Offset  File/Offset 
1        0x40100000   0x00000a20   2592         0x00000008   8           
2        0x3ffe8000   0x000002fc   764          0x00000a30   2608        
3        0x3ffe82fc   0x000002a4   676          0x00000d34   3380        
Padding: 16 bytes ->  0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x22
Calculated Checksum: 0x22  [matches]
                      Size/Hex     Size/Bytes   File/Offset  File/Offset 
Blank    0xff         0x00000010   16           0x00000ff0   4080        
Header:  0xea 4 0x0 0x1 0x40100004
Segment  Offset       Size/Hex     Size/Bytes   File/Offset  File/Offset 
1        0x00000000   0x0005e0f0   385264       0x00001008   4104        
Header:  0xe9 3 0x0 0x20 0x40100004
Segment  Offset       Size/Hex     Size/Bytes   File/Offset  File/Offset 
1        0x40100000   0x000076dc   30428        0x0005f108   389384      
2        0x3ffe8000   0x00000874   2164         0x000667ec   419820      
3        0x3ffe8880   0x000023bc   9148         0x00067068   421992      
Padding: 4 bytes ->  0x0 0x0 0x0 0x69
Calculated Checksum: 0x69  [matches]
                      Size/Hex     Size/Bytes   File/Offset  File/Offset 
Unknown               0x00000008   8            0x00069430   431152      
0x42 0xd 0x2b 0x65 0xff 0xff 0xff 0xff
                      Size/Hex     Size/Bytes   File/Offset  File/Offset 
Blank    0xff         0x00017bc8   97224        0x00069438   431160      
Header:  0xea 4 0x0 0x2 0x40100004
Segment  Offset       Size/Hex     Size/Bytes   File/Offset  File/Offset 
1        0x00000000   0x0005e0f0   385264       0x00081008   528392      
Header:  0xe9 3 0x0 0x20 0x40100004
Segment  Offset       Size/Hex     Size/Bytes   File/Offset  File/Offset 
1        0x40100000   0x000076dc   30428        0x000df108   913672      
2        0x3ffe8000   0x00000874   2164         0x000e67ec   944108      
3        0x3ffe8880   0x000023bc   9148         0x000e7068   946280      
Padding: 4 bytes ->  0x0 0x0 0x0 0x68
Calculated Checksum: 0x68  [matches]
                      Size/Hex     Size/Bytes   File/Offset  File/Offset 
Unknown               0x00000008   8            0x000e9430   955440      
0x98 0x88 0x89 0x38 0xff 0xff 0xff 0xff
                      Size/Hex     Size/Bytes   File/Offset  File/Offset 
Blank    0xff         0x00012bc8   76744        0x000e9438   955448      
                      Size/Hex     Size/Bytes   File/Offset  File/Offset 
Unknown               0x00000080   128          0x000fc000   1032192     
0x5 0x8 0x4 0x2 0x5 0x5 0x5 0x2 0x5 0x0 0x4 0x5 0x5 0x4 0x5 0x5 0x4 0xfe 0xfd 0xff 0xf0 0xf0 0xf0 0xe0 0xe0 0xe0 0xe1 0xa 0xff 0xff 0xf8 0x0 0xf8 0xf8 0x4e 0x4a 0x46 0x40 0x3c 0x38 0x0 0x0 0x1 0x1 0x2 0x3 0x4 0x5 0x1 0x0 0x0 0x0 0x0 0x0 0x2 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0xe1 0xa 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x1 0x93 0x43 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x1 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
                      Size/Hex     Size/Bytes   File/Offset  File/Offset 
Blank    0xff         0x00002f80   12160        0x000fc080   1032320     
File Size: 1044480  Calculated: 1044480  [matches]

From that output, one can see where (address) and how each of the single images are placed in the ‘combined’ image.  Now that the combined image format is known, a script can be coded to create it from the individual files.

12V to 5V Sensor Circuit

A number of sensors are powered via 12 Volts. Some, however, need 5 Volts.  To power a remote DHT11 temperature sensor, I created a 12V to 5V sensor circuit.

This is the wiring diagram:

12V to 5V Sensor Circuit

Get the Fritzing code for it HERE.

DHT11

DHT11

 

 

 

The DHT11 needs 5 volts,  ground and data. It plugs into the 12V to 5V sensor circuit via a 4 pin header slot. Notice there is a 10K pull-up resistor connected to data.

Here is what the circuit looks like with and without the DHT11 plugged in:

12V to 5V Sensor Circuit Side View12V to 5V Sensor Circuit with DHT11

This type of circuit comes in handy for a larger project such as an alarm or monitoring system where sensors are some distance away from the main monitoring device such as an Arduino.  For longer distance cable runs, Cat 5  cable is used which has 8 wires.  Three wires can be twisted together for 12 volts. Three wires can be twisted together for ground and the remaining 2 wires can be twisted together for data.  The multiple wires together increases the ‘wire size‘ and overcomes signal loss and interference in most cases.

Load more