Compact Remote Alarm Transceiver – Part 1

As part of my experiments with LoRa and the easy to use ATMega328P, I have recently designed quite a few LoRa based projects. In this final 2 part series, I will look at two additional projects, part of a Remote Alarm Transceiver, where I experimented with a changing a few things:

– Using LM317G adjustable voltage regulators.
– Replacing my standard N-MOS based logic level converters with a dedicated chip.

Remote Alarm Transmitter
LoRa Remote Alarm Transmitter – with Onboard Relay putout and two sensor inputs

How does this differ from my other LoRa Based projects?

The PCB presented above does in fact not really differ a lot from any of my existing LoRa based projects.
However, there are a few subtle changes, mainly experimental changes, brought on by factors such as component availability and an attempt to reduce component counts and board size.

The first of these changes is using the LM317G voltage regulator, in the place of my usual LM1117 3.3 and 5.0 LDO regulators.

The LM317 is an old device, It has been on the market for a long time. It can supply up to 1.5A of current, and a single device can be configured to supply a wide range of different voltages by just changing two resistors. This seemed quite attractive to me, as it is getting quite difficult to reliably get quite a few components on time, and with decent pricing in the post-Covid-19 world.

The second major change would be moving away from my existing N-Mos based Logic converter setup, where I used the BSS138 and 10K resistors as logic converters. This setup works perfectly, but it has the drawback of requiring quite a lot of components. for example:

To provide logic conversion to an RA-02 module, with access to all the IO Lines (GPIO0-5 included) required 12 BSS138 Mosfets and 24 10k resistors. This is quite a lot of components. A dedicated logic converter chip would thus be a much more attractive solution.

Driver circuitry for sensor Inputs, consisting of a simple transistor input, and an optically isolated Relay output completes the circuit.

Using the LM317

LM317 Typical use circuit - Fixed Voltage

The output voltage of the LM317 is typically set using two resistors, with a suitable current rating, using the following Formula

VOUT = 1.25 * ( 1 + R2/R1 )

It is also common to use a variable resistor at R2, to have fine control over the output voltage. This is due to the fact that stock resistor values do not always give you the exact voltage you require. You should also take into account that using a 5% resistor will be less accurate than a 1% resistor.

The grid below is a list of common stock resistor values for R1/R2, with the resulting voltage produced.

R1 vs R2 Grid for use in selecting fixed output voltage

R2\R1150180220240270330370390470
681.821.721.641.601.561.511.481.471.43
821.931.821.721.681.631.561.531.511.47
1002.081.941.821.771.711.631.591.571.52
1202.252.081.931.881.811.701.661.631.57
1502.502.292.102.031.941.821.761.731.65
1802.752.502.272.192.081.931.861.831.73
2203.082.782.502.402.272.081.991.961.84
2403.252.922.612.502.362.162.062.021.89
2703.503.132.782.662.502.272.162.121.97
3304.003.543.132.972.782.502.362.312.13
3704.333.823.353.182.962.652.502.442.23
3904.503.963.473.283.062.732.572.502.29
4705.174.513.923.703.433.032.842.762.50
5605.925.144.434.173.843.373.143.042.74
6806.925.975.114.794.403.833.553.433.06
8208.086.945.915.525.054.364.023.883.43
10009.588.196.936.465.885.044.634.463.91
120011.259.588.077.506.815.805.305.104.44
150013.7511.679.779.068.196.936.326.065.24
180016.2513.7511.4810.639.588.077.337.026.04
220019.5816.5313.7512.7111.449.588.688.307.10
270023.7520.0016.5915.3113.7511.4810.379.908.43
330028.7524.1720.0018.4416.5313.7512.4011.8310.03

As you can see from the table above, using stock resistors, the output voltage is reasonably accurate, but it is quite obvious that you will need a potentiometer to get exact values.
Another issue will definitely be heat dissipation. In my PCB design, I have used the SOT-223 package of the component, with a PCB heatsink, built directly into the layers. With the LM1117 LDO regulators, these work extremely well.

Logic Level Conversion

In this design, I used my standard Logic Level conversion circuit, comprised of a BSS138 N-Mos with two 10 k resistors. This circuit, although a bit cumbersome with lots of components if you need many logic converters, is very stable, and functions extremely well.

Conclusion

This circuit was designed as a two-part prototype, with the goal of experimenting with different voltage regulators, and in part 2, with a single chip 8 channel logic converter. As such, I do not feel comfortable releasing the full schematics to you at this stage, do so anyway in the interest of learning. The circuit works, but there are many issues with the regulators:



– Overheating at input voltages above 8.0v
The PCB heatsink will have to be improved, or even a different package for the LM317 with the possibility to attach an external heatsink.

– The voltages do not seem stable, especially on the 3.3-volt side.

Manufacturing the PCB

PCBWay

This PCB was manufactured at PCBWAY. The Gerber files and BOM, as well as all the schematics, will soon be available as a shared project on their website. If you would like to have PCBWAY manufacture one of your own, designs, or even this particular PCB, you need to do the following…
1) Click on this link
2) Create an account if you have not already got one of your own.
If you use the link above, you will also instantly receive a $5USD coupon, which you can use on your first or any other order later. (Disclaimer: I will earn a small referral fee from PCBWay. This referral fee will not affect the cost of your order, nor will you pay any part thereof.)
3) Once you have gone to their website, and created an account, or login with your existing account,

4) Click on PCB Instant Quote

5) If you do not have any very special requirements for your PCB, click on Quick-order PCB

6) Click on Add Gerber File, and select your Gerber file(s) from your computer. Most of your PCB details will now be automatically selected, leaving you to only select the solder mask and silk-screen colour, as well as to remove the order number or not. You can of course fine-tune everything exactly as you want as well.

7) You can also select whether you want an SMD stencil, or have the board assembled after manufacturing. Please note that the assembly service, as well as the cost of your components, ARE NOT included in the initial quoted price. ( The quote will update depending on what options you select ).

8) When you are happy with the options that you have selected, you can click on the Save to Cart Button. From here on, you can go to the top of the screen, click on Cart, make any payment(s) or use any coupons that you have in your account.

Then just sit back and wait for your new PCB to be delivered to your door via the shipping company that you have selected during checkout.

Special Limited Offer – Get your PCB for only $1

PCB for only $1

Every once in a while, we get an opportunity to take advantage of an exceptional deal. Recently, the friendly people at PCBPartner.com reached out to me with another great deal… “Get your next PCB for only $1!”

You will get the following:
10 FR-4 PCB, 1 or 2 layers, with Green Soldermask, in dimensions not exceeding 100mmx100mm, Manufactured and delivered to your door for only 1 (one) $USD.
This promotion will be valid until 31 May 2022, with AIRMAIL shipping included.

Promotion from PCBPartner

Who is PCBPartner.com

PCBPartner.com is owned and operated by Kinji Group, established in 1997. We have over 20 years of experience in PCB manufacturing, PCB design, component manufacturing and distribution, PCB assembly and PCB CAD software development.

While Kinji Group has 3 PCB factories in China, we have also developed strategic partnerships with more than 15 other factories around Asia. We, therefore, have a large group of specialists in PCB manufacturing, quality control, technical support and part sourcing to support your innovative ideas and products.

Our over 500 employees are spread across 8 branches in Mainland China (Shenzhen, Dongguan, Shanghai, Wuxi, Chengdu, Xiamen), Hong Kong SAR, and Taiwan. And we’re still growing!

Why Should you use PCBPartner for your next PCB order?

Variety of PCB Material

Up to 54 layers during PCB manufacturing.

FR-4 with different tg (135,150,170),
CTI 600 (Halogen free) PCB material,
Aluminium base PCB,
Flexible PCB,
Rigid-Flexible PCB,
Rogers, and many more materials.
Whatever material you need, they can deliver.

Special PCB Specifications:

Regular PCB production for these special PCB specifications

● Impedance control

● Custom-layer stack up

● 1-3 oz copper thickness for outer/inner layers

● Hard gold, ENEPIG, OSP surface finish

● Two different solder mask colours on one PCB

● Edge connector plating

● Edge plating

● Via filled with aluminium/resin

● And more…

Fast Lead Time

Turnaround is quick as prototypes usually take as little as 24 hours to produce, with a direct pickup service from their warehouse. They partner with DHL and FedEx for this to make sure you get your PCB in the shortest time possible. They also offer express production options for small and medium-sized batches so that you get your boards EVEN quicker.

Competitive Pricing

Get competitive pricing on prototypes and your future mass production. For any mass production orders, contact them so that they can find the best pricing and shipping solutions to maintain your competitiveness

Ordering boards in bulk? Save more the larger your order:

US$10 off orders of 100+

US$10 off orders of 100+

US$50 off orders of 500+

US$120 off orders of 1000+

Quality Control

After 20 years, they have built a strict standard of quality – their quality control teams ensure all PCBs pass the AOI and E-test with 100%.

Technical Support

They have an experienced PCB manufacturing and engineering team to help you detect potential mistakes in your Gerber files. They also use – and have used for 15+ years – a reliable PCB testing software solution, iPCB, to detect mistakes in designs.

Excellent Customer Service Team

They have a passionate (and patient!) customer service team to help you solve any problems while ordering, handle engineering questions, or answer any after-sales service request. They will respond to you within 1 working day, that’s their customer service guarantee.

Flexible Payment Term

If you have a large amount of PCB to order every month and want to work with them for AMS 30 payment, they will be happy to sign an agreement with you.

Promotion from PCBPartner

Easy to use CAN-BUS Module with Relay, LiPo Battery Backup

Can Relay Module running on battery power

CAN-Bus allows us to add a lot of devices to a single bus (theoretically up to 127, with a practical limit of about 110). It was logical to decide to use CAN-BUS for communication between my LoRa-CAN Module and remote nodes in my ongoing farm telemetry system. In a previous project, I introduced the LoRa-CAN Gateway, which will be used to send/receive messages from the master control unit, the SX127x-RA-02-Module.

Today, I will focus on the design of the node device, an Arduino compatible CAN Relay Module PCB, with a built-in, CAN Controller and Transceiver ( MCP2515 and TJA1050 ), and the option to be powered from a LiPo battery (18650 or Single 3.7v Lipo cell), with charging provided by an MH-CD42 Module, similar to that used in the SX128x project mentioned above. The Module can also be powered directly from up to 12v DC by placing Jumper H2 in the V-REG position…

As mentioned in a previous project, the MH-CD42 module can provide up to 2A of current to charge a battery and provide 5V DC on a direct bypass circuit to power the rest of the circuit while the battery is being charged. This feature makes it ideal for my intended use, as I would only be needing battery power at night when the off-grid solar powered inverter is not actively charging the main batteries, and providing 220v AC.

Can Relay Module Top view

The CAN Relay Module PCB was designed to be as compact as possible, with a total footprint of the standard Arduino UNO. As space was at a premium, and to ensure that there are the absolute minimum additional components that will consume power when operating from the battery, I have not included any USB-to-Serial converters onboard. Firmware can be uploaded with an AVRASp, USBASP, Arduino as ISP or even an external USB-to-Serial converter ( providing that you load a bootloader into the Atmega Chip).

Can Relay Module side view

All of the unused GPIO pins on the ATMEGA chip is broken out into headers, with the exception of D10 ( which is used as CE/SS on the MCP2515 CAN controller), D9 which is connected to the Interrupt from the MCP2515, and D4, which is used to control the onboard Relay.

Can Relay Module with LiPo cell and MH-CD42 Module

The MH-CD42 Module, and LiPo or 18650 Battery are completely optional, The device can function without these, by moving the H2 jumper to the VREG position as already mentioned above. This will divert the DC Voltage(Up to 12v) from the DC input adapter to the build-in 5v LDO voltage regulator to power the device.

If you place the H2 Jumper in the VBAT position, you need to install the MH-CD42 module to provide power to the rest of the PCB, as well as keep the LiPo battery or 18650 Cell charged…

Please NOTE:

When the MH-CD42 module is in use, the total DC input voltage through the DC input adapter SHOULD NOT exceed 5.5v DC! This is a limitation in the operating parameters of the MH-CD42 module. Not paying attention to this will result in damage to the MH-CD42 Module.

You could also power the module with REGULATED 5v DC, directly from any 5v header pin. Please note that in this case,

1) The battery won’t be charged.

2) The LDO regulator will not be in operation.

The Microcontroller

The CAN Relay Module can use any of the ATMEGA8a/88/168/328 AVR microcontrollers, as the pinout is identical. I believe this is an advantage, especially with the current chip prices, where my last quote for an ATMEGA328P-AU was 69$USD!!!! This is in comparison to the 4 to 5 USD each for an ATMEGA8a or ATMEGA168 ( We must also remember though that the 328p is very well known, and thus have higher demand. The 8a/88 or 168 are less well known, have much less memory and flash area, and will thus be cheaper. A word of action though, I had strange issues with I2C on the Atmega8a with Mini core, to such an extent that I2C does not work at all?

If you plan to use I2C on this board, install a 328p right from the start, and save yourself a lot of headaches!

Can Relay Module Schematic Page 1

This is the Relay driver circuit. As you can see, it is optically isolated, and active LOW. This means that you have to pull D4 LOW to energise the relay. Also note that, although the relay is optically isolated from the microcontroller, the coil is NOT galvanically ISOLATED. The load, which is magnetically switched, will be truly galvanically isolated, providing of course that you don’t do something silly like using a common ground to the PCB as a common on the relay…

Can Relay Module Schematic Page 2

This is the Processor and Power-supply schematic. The circuit is basically a standard Arduino Nano, with modifications for the CAN Controller on the next schematic page. Note that the MH-CD42 is not shown on the schematic. The VBAT net connects directly to the VIN pin on the unit, with the BAT net connecting to the positive of the battery. VOUt-5v from the module goes directly to the 5v net. All grounds are commoned.

This is the CAN Controller and Transceiver circuitry. The MCP2515 connects directly to the SPI bus on the microcontroller via D13, D12 and D11, with D10 as CE/SS and D9 as Interrupt or IRQ. It is important to note that although the MCP2515 is a 3v capable device, the TJA1050 Can transceiver is 5v only. This prevents us from running the PCB at 3v unless, of course, we change the TJA1050 out for a 3v capable device…

CODE

The board has been extensively tested with Cory J Fowler’s mcp_can Arduino Library. It works very well indeed.

As I am currently working on the final integration of the two modules, I am not yet ready to publish my final code, showing the operation between the CAN-Bus and LoRa-to-CAN Gateway device in this post. Once I am happy that all issues are definitively sorted out, I will publish my code.

This PCB was manufactured at PCBWAY. The Gerber files and BOM, as well as all the schematics, will soon be available as a shared project on their website. If you would like to have PCBWAY manufacture one of your own, designs, or even this particular PCB, you need to do the following…
1) Click on this link
2) Create an account if you have not already got one of your own.
If you use the link above, you will also instantly receive a $5USD coupon, which you can use on your first or any other order later. (Disclaimer: I will earn a small referral fee from PCBWay. This referral fee will not affect the cost of your order, nor will you pay any part thereof.)
3) Once you have gone to their website, and created an account, or login with your existing account,

4) Click on PCB Instant Quote

5) If you do not have any very special requirements for your PCB, click on Quick-order PCB

6) Click on Add Gerber File, and select your Gerber file(s) from your computer. Most of your PCB details will now be automatically selected, leaving you to only select the solder mask and silk-screen colour, as well as to remove the order number or not. You can of course fine-tune everything exactly as you want as well.

7) You can also select whether you want an SMD stencil, or have the board assembled after manufacturing. Please note that the assembly service, as well as the cost of your components, ARE NOT included in the initial quoted price. ( The quote will update depending on what options you select ).

8) When you are happy with the options that you have selected, you can click on the Save to Cart Button. From here on, you can go to the top of the screen, click on Cart, make any payment(s) or use any coupons that you have in your account.

Then just sit back and wait for your new PCB to be delivered to your door via the shipping company that you have selected during checkout.

CAN-to-LoRa gateway using RA-02, MCP2515 and ATMEGA328P

ATMEGA328P Module with integrated LoRa and CAN-BUS

INTRODUCTION

In my quest to perfect my LoRa telemetry system, I have gone through quite a few prototypes by this time. This post will focus on the next node design. Due to the fact that the area where I will deploy the system is quite large, but with roughly square boundary fence-lines, I decided to try and reduce the number of LoRa Radio nodes needed to cover the entire area. This opened up the opportunity to utilise CAN-BUS to attach sensor-only nodes to a Radio node and have them report status on exception as well as on requests from the radio node.

The device will thus function as a LoRa-to-CAN-BUS Gateway, with some local automation to control the transmission of data to the master station. This concept can also be adapted for use in other areas, such as home -automation, or an industrial setting.

At the heart of the device, I have stuck with the versatile ATMEGA328P, which, current chip shortages excluded, and current high prices excluded, are a very inexpensive chip, with lots of well-tested libraries, and a relatively low learning curve, largely due to its very wide use in the Arduino ecosystem.

The LoRa component is handled by the RA-02 or even RA-01H module, from AI-Tinker (not sponsored). This device, as we have seen in the previous prototypes, requires the use of logic level converters, due to the fact that it only accepts 3.3v logic levels. While I could get rid of those if I powered the ATMEGA328P from 3.3v, it would give rise to two problems, of which one will still force the use of level converters…

I chose to run the ATMEGA328P at 16Mhz, which basically forces me to use 5v to power the chip. The second reason is not so obvious unless you read a few datasheets very thoroughly…

The CAN-Bus component is handled by the MCP2515 Standalone SPI-to-CAN Controller, as well as the TJA1050 CAN Bus transceiver.

This is where things get interesting… The MCP2515 can operate on 3.3v, but the TJA1050 is a 5v only device. I could thus in theory use logic level converters only between the MCP2515 and the TJA1050, while running the rest of the circuit on 3.3v…

Given that I would rather run the ATMEGA328P on 16Mhz, as well as the fact that my LoRa Radio Module circuit, with its logic level converter circuitry, works extremely well, I decided not to change that, and keep the CAN Bus running at 5v all the way through, as I would still have to use a 5v regulator on the PCB anyway just for that purpose.

IO connections for LoRa and CAN BUS modules

You can order this PCB directly from PCBWay, by clicking here

Both of the two integrated components ( Lora and CAN ) are SPI devices. This means that they share common SCK, MISO and MOSI lines ( provided on the ATMEGA328P by pins D13, D12 and D11 respectively. The individual SPI device is then further selected for operation by the use of a CE pin, one unique pin per device, which is pulled low by the MCU to indicate to the device that it should pay attention to the data being transmitted on the SPI bus…

Both LoRa and CAN makes use of other pins as well, LoRa needs a Reset pin, connected to D9, a CS/CE Pin on D10 as well as a hardware interrupt pin, connected to D2. ( Note that this is for use with Sandeep Mistry’s LoRa Library. The Radiolib library would require an additional pin, usually connected to DIO1 on the LoRa module. The device does not provide access to those pins in its current layout, so you can only use it with the Sandeep Mistry library, for now at least… )

The CAN module uses a CE/CS pin at D4, with an IRQ pin on D6, which, although not a hardware interrupt pin, does have PCINT functionality.

Pins D10, D9 and D2 are not broken out for user access. although I decided to give access to D4 and D6, as well as the SPI bus, D11, D12, D13, to allow interfacing with logic analysers, or adding other SPI devices to the bus…

This brings us to a very interesting point… Does the two SPI devices actually play nice together? and what do I mean by “playing nice together”?

To answer that question, we are forced to first look at a bit of theory, as well as understand the fundamental differences between SPI and I2C…

The Difference between SPI and I2C

Most of us will be quite familiar with I2C, as it is a very common protocol used to connect sensors to a microcontroller. It consists of only two IO lines, SDA for data, and SCL for the clock. Each device on the bus has its own built-in address, like in the case of a PCF8574 IO expander, this address can be selectable between 0x20h and 0x27h. All of the devices share these common data lines, and will only respond when specifically addressed by the master controller… Unless you accidentally put two devices with the same address on the same bus, (if that would even work), there is no way that the wrong device would respond to any request for data…

SPI on the other hand, operates on a completely different principle, making it quite a few times faster than I2c, with data being simultaneously sent and received by the active device… SPI is also known as a four-wire protocol. Each device has a minimum of 4 data lines, namely SCK ( clock), MOSI ( for data transmitted FROM the Master TO the slave device ), MISO ( for data transmitted TO the master FROM the slave device) and a CE or CS ( Chip select ) pin.

SCK, MISO and MOSI are COMMON to all devices, meaning it is shared between all of them. CE/CS is a unique pin for EACH device, meaning that if you had four SPI devices on a bus, you would have to have four individual CE/CS pins!

A device will, or rather should only respond to data on the SPI-BUS IF the master pulls its respective CE/CS pin LOW. It should now very quickly become clear to you that this can become a very very complex mess, very quickly.

Let us take a very good example. the ST7789 SPI display module, has a cheap version, commonly sold on Ali-express, as we ll as other online stores. This particular module, I assume in a bid to make it easier to use, has the CE/CS pin internally pulled down to ground by default… So what about that, you may ask? What is wrong with that, as it saves you an IO pin?

It is in fact very wrong, a fact that you will very quickly discover if you ever tried using one of those displays on an SPI bus together with other SPI devices… Nothing will work, or only the display will work ( if you are lucky)

But why?

Pulling CE/CS LOW, signals to the chip that it should respond to instructions on the common SCK, MISO and MOSI lines. having the pin internally pulled LOW, thus forces that chip to always respond, even when it should not. Thus contaminating the entire SPI-BUS with garbage…

The answer to the question

After that very long-winded explanation, which is still extremely basic, it is time that we get back to our original question:

Does the Sx127x ( RA-02 ) Module and the MCP2515 Can Controller play nice on the same bus? The answer is not straightforward, as it comes down to which libraries you use…

Remember that the library must pull down the CE/CS pin of the device that it wants to communicate with. Some libraries wrongly assume that they are the only ones in use, and ignores the simple fact, that they should release the CE/CS pin AFTER EVERY transaction, to free up the bus for other devices to use it as well…

After extensive testing, I can however say that Sandeep Mistry’s LoRa Library, as well as the mcp_can library, does indeed play nice together. These two libraries do not keep the individual CE/CS pins pulled LOW, and allows the spi bus to be shared.

This is not the case with the ST7789 Module discussed above, where the hardware actually pulls the pin ow the entire time…

Taking a closer look at the PCB

Let us take a closer look at the PCB. The Ra-02 Module ( LoRa ) dominates most of the left-hand side of the PCB, with the ATMEGA328P on its right. The RA-02 is surrounded by the level converters, using the BSS138 N-Channel Mosfet, and 10k resistors (Q1 to Q6, R1, R2, R3, R4, R5, R6, R8, R9, R10, R11, R12, R13)

C1 and C2 are bypass capacitors for the Ra-02 module

In the bottom left corner, we have a hardware reset button, to reset the ATMEGA328P, with a yellow jumper (H1) next to it. This jumper controls the 120ohm ballast resistor (R17) for the CAN bus. Removing the jumper will remove the ballast. Directly below that is the CAN connecter, marked as U5, with CH as CAN-H, and CL as CAN-L ports.

U3 and U4, together with R18, R19, X2, C16, C17 make up the CAN components on the PCB. Decoupling is provided by C6, C7, C8 as well as C9 and C12 ( Includes the ATMEGA328P’s decoupling as well )

An ICSP programming header is provided above U1 ( ATMEGA328P) for use with USPASP, AVRASP or Arduino as ISP and similar.

No USB to serial converter is provided on the board, Serial upload is possible is loaded with an Arduino bootloader for the Arduino NANO ( to make use of all the analog inputs). RxD, TxD and DTR pins are broken out on opposite sides of the PCB, as well as access to 3.3v, 5v and GND pins.

A DC power socket is provided. it can accept up to 12v DC, although I would recommend not to go over 7.2 volts, to not stress the LDO regulators, at the back of the PCB ( LDO1, and LDO2) too much.

You can order this PCB directly from PCBWay, by Clicking here

in the picture above, I have connected a USB-to Serial converter, as well as CAN-BUS to the device.

Schematic Diagram

The detailed schematic diagrams are provided below:

Sheet 1 (above) takes care of the ATMEGA328p and it supporting circuitry, as well as the power supply via LDO regulators.

Sheet 2 (below) takes care of the Logic Level converters, RA-02 (Sx1278) LoRa Module, and CAN-BUS controller and transceiver circuitry.

Software and Firmware

In order to test this module, I made use of the mcp_can library by Cory J Fowler, for the CAN-Bus part,

as well as Arduino-LoRa by Sandeep Mistry

A combined example utilising both LoRa and CAN at the same time, will be released with the next part of the project, namely the CAN-Relay Module

This PCB was manufactured at PCBWAY. The Gerber files and BOM, as well as all the schematics, will soon be available as a shared project on their website. If you would like to have PCBWAY manufacture one of your own, designs, or even this particular PCB, you need to do the following…
1) Click on this link
2) Create an account if you have not already got one of your own.
If you use the link above, you will also instantly receive a $5USD coupon, which you can use on your first or any other order later. (Disclaimer: I will earn a small referral fee from PCBWay. This referral fee will not affect the cost of your order, nor will you pay any part thereof.)
3) Once you have gone to their website, and created an account, or login with your existing account,

4) Click on PCB Instant Quote

5) If you do not have any very special requirements for your PCB, click on Quick-order PCB

6) Click on Add Gerber File, and select your Gerber file(s) from your computer. Most of your PCB details will now be automatically selected, leaving you to only select the solder mask and silk-screen colour, as well as to remove the order number or not. You can of course fine-tune everything exactly as you want as well.

7) You can also select whether you want an SMD stencil, or have the board assembled after manufacturing. Please note that the assembly service, as well as the cost of your components, ARE NOT included in the initial quoted price. ( The quote will update depending on what options you select ).

8) When you are happy with the options that you have selected, you can click on the Save to Cart Button. From here on, you can go to the top of the screen, click on Cart, make any payment(s) or use any coupons that you have in your account.

Then just sit back and wait for your new PCB to be delivered to your door via the shipping company that you have selected during checkout.

I2C IO Module with 4 Relay Outputs and 4 Galvanic Isolated Inputs

Sometimes we need extra Inputs or Outputs on a device, or for use with a project. To implement it properly we also need a lot of additional electronic components to properly isolate these inputs and outputs, with the signals they switch, from our own project, because, let us be real, electronics and electrical devices in the real world do not all work with Arduino or ESP32/ESP8266 save voltages ( 5v and 3.3v ).

I will also tell you about a very special deal to get PCBs of your own made for only one (1) USD ( Including shipping with DHL )! No, I am not joking, and I am not crazy either… More on that later in the post…


It is thus extremely important to have a module that can effectively interface with inputs of 5.5v up to 32v DC ( optically Isolated up to 3000v ), and relay outputs, also optically isolated at 3000v. ( Note that the optical isolation voltage does not mean you can input that voltage level into the chip! It means that it can isolate the electronics on the safe side of the isolator from a voltage spike of up to that voltage!).

I also love using I2C, as it allows me to add modules onto an existing data bus while using only 2 GPIO lines on the MCU!

The module I am presenting to you today was designed to be operated from 5v DC. That includes the I2C data lines (SDA and SCL). If you need to interface to a 3.3v microprocessor, like an ESP32 or ESP8266, or even the new RP2040 or an STM32, you need to use a logic level converter.

The PCB uses the popular PCF8574 8 channel IO expander, which is extremely easy to use, and where you can connect up to 8 devices in a chain ( 16 if you use the PCF8574AT variant as well.. Meaning eight of each variant) This surely adds up to quite a lot of IO lines at a cost of only 2 GPIO on your MCU!

The Circuit diagram is below, and I will discuss each part briefly.

Schematic – Page 1

This is the Galvanic Isolated Input schematic. Each input operates at a voltage of 5.5v to 32v DC. Complete Galvanic Isolation between the Module and the remote input is in effect. Please note that you have to supply a remote ground from the device that provides the input. DO NOT connect the PCB Module ground to an isolated ground pin. This may still work but renders the galvanic Isolation for that input completely useless.


Relay Driver Schematic

This is the Relay driver schematic. Each relay output is driven through an optocoupler, as well as a transistor. Although this arrangement does not provide complete galvanic Isolation of the relay coil, it does protect your MCU from any voltage spikes caused by back-emf when the relay is de-energised. The Relay contacts themselves, being magnetically actuated by the coil, are in themselves Galvanically Isolated from the rest of the PCB.

I2C Control Schematic

Finally, we have the I2C IO Expander schematic, with a 5v LDO regulator, capable of providing up to 600mA of current to the PCB. The PCF8574 Chip’s address is selectable with DipSwitch SW1 so that you can use multiple PCBs at the same time if you should choose to do so. The only note on that is that you should not connect the 5v lines of each individual PCB together. You should also only connect the GND and SDA, SCL lines back to the MCU.

Raw PCB Layout

Earlier on in the post, I promised to tell you about a very special deal…

Well, here it is, as well as some details about the sponsor of this very exciting deal…

PCBPartner.com is owned and operated by Kinji Group, which was established in 1997. We have over 20 years of experience in PCB manufacturing, PCB design, component manufacturing and distribution, PCB assembly and PCB cam software development.

While Kinji Group has 3 PCB factories in China, we have also developed strategic partnerships with more than 15 other factories around Asia. We, therefore, have a large group of specialists in PCB manufacturing, quality control, technical support and part sourcing to support your innovative ideas and products.

Our over 500 employees are spread across 8 branches in Mainland China (Shenzhen, Dongguan, Shanghai, Wuxi, Chengdu, Xiamen), Hong Kong SAR, and Taiwan. And we’re still growing!


We’re confident once you try us out, we’ll become your PCB Partner. And if not? Well, you’ll have scored some free PCB! So why not take us for a spin, you’ve got nothing to lose.

We, MakerIoT2020.com, have decided to give it a go and send this particular PCB to PCBPartner.com for manufacturing. So far, while we are still waiting to receive the PCB, ( Weekends happen 🙂 ), We are very happy with the ease of use of the online ordering system provided.

We would also like to point out that this special order will only be available until the end of March 2022,
as well as that there are a few conditions:

Promotion ends  March 31st 2022
Each new customer can enjoy free PCB on their first order
This promotion applies to
1-2 layers of FR4 PCB, up to 100x100mm, 10pcs, with Green Solder Mask
4 layer of FR4 PCB, up to 50x50mm, 10pcs, with Green Solder Mask
1 layer Aluminum PCB, up to 100x100mm, 10pcs
This PCB promotion cannot be used with other discounts or other promotional activities



For a full list of conditions, and countries that may participate in this offer, please click on the link here

Let us have a look at the entire ordering process..

Once you click on the PCBPartner.com link, you will be taken to their website, where you should sign up, which is free and easy… We used our Google.com account details and were ready to order in seconds…

PCBPartner Start Page

You can now Login with your new credentials ( after registering using this special link ). Then click on the FR4 button to start the order process…

FR4 PBC Quote Form – Before uploading your Gerber Files

Enter the specific details for the manufacturing of your PCB, and upload your Gerber files.

After uploading your Gerber Files.

Continue selecting options for your PCB order…
Make sure to select DHL shipping, to take advantage of the special 1USD option, and click on the ADD to Cart Button…

Quote added to your shopping cart.

You will now get a message that your enquiry has been submitted successfully.

Click on the “Under review” button, to see your quote status… In my case, it took about 5 minutes for the review to pass, and be able to checkout and pay for the order…

PCB order under review

Once the review has passed, you will see a pending payment,

Payment Pending

You may now click on the “Proceed to Payment” option

Add your shipping address, and choose your payment option.

At this moment in time, only two payment options are supported, Paypal ( as well as Debit and Credit cards) and Direct Bank Transfer. I believe more options will be made available in future..
Checkout with Paypal

In my case, I chose Paypal and paid by Debit card.

Enter your card details
After Payment.

After payment was made successfully, you can also check on the status of your order…

Review your order status

You can also review your order at any stage before or after payment, as well as get progress reports of the manufacturing process.

PCB Order Status.

In conclusion, I would like to say that it was quite easy to order and make payment. The Website is easy to use, and everything is clear and easy to understand. The PCB was well manufactured and seems to be quite good quality

Sx127x-Ra-02-Test-Module with ATMEGA328P-AU

SX127x LoRa/FSK/OOK Module with LiPo battery-backup

SX127x LoRa/FSK/OOK Prototype Radio Board

I recently had a requirement to do some automation/telemetry on the farm. Things went missing, unauthorised persons were trespassing on the property, helping themselves to eggs, chickens, ducks and produce. Something had to be done, and as there is nobody sleeping there at night, it had to be possible to get remote status updates in real-time.

The farm is also completely off-grid, with solar-powered inverters taking care of all the water and electricity needs. Power usage monitoring could thus be a great help as well…

The idea is as follows:

Have a central control station, with various remote devices to do intrusion detection, control lights and water pumps, as well as monitor the battery levels of the inverter and solar-panel system. As the area is quite large, having to pull in electrical cabling will not be feasible, nor could the inverter handle all of that.

I decided to use the RA-02 LoRa/FSK/OOK module, on a custom PCB, but with various different PCB modifications, to take care of each stage of the project. The PCB that I will present today will be mainly used as the control unit, but it could also be a remote station, depending on what options are needed.

The ATMEGA328P-AU MCU is used as the main processor on each board, with the MH-CD42 Boost Converter/LiPo battery charger module taking care of power supply requirements. This module can source up to 2A at 5.0v, More than adequate for my needs. The ATMEGA328P will be put into sleep mode, to wake on interrupt to respond to events as needed ( To help save power ). Power usage of the circuit is around 50mA in standby ( NOT SLEEP MODE ), and with a peak of 100mA on a LoRa Transmit or Receive event.

The RA-02 Module did however present some challenges, as it is a 3.3v device, with non-5v capable IO lines. This made it necessary to include a 3.3v LDO regulator, as well as logic level converting circuitry onto the PCB. To allow for the most flexibility, all DIOs on the RA-02 was also broken out via level converters, in addition to the required SPI pins ( MOSI, MISO, CE and SCK ). This amount to a total of 11 level converters onboard.

I chose the MH-CD42 Power module for the reason that it can supply current at the same time as charging the LiPo battery (in my case, I used a 18650 cell). This will help greatly, as 220v inverter power is available during the day to charge the batteries.

PCB fitted to enclosure

The PCB can be ordered at PCBWay by clicking here
This image has an empty alt attribute; its file name is Screenshot-from-2021-07-22-18-45-42.png

Another challenge was definitely the enclosure. I found some really nice enclosures online, but the mounting holes were located in a very particular pattern, making it necessary to do some very careful measuring to get the PCB to fit exactly. The enclosure also has space to accept the other PCB modules, like the USER Control panel, and other sensor devices as needed. PCBWay did a great job at manufacturing the PCB so as to fit exactly. I must admit that I had quite a few tense moments between sending the PCB off to manufacturing and receiving it, as to whether my measurements were actually accurate, and if the PCB would fit as I imagined…

As you can see in the picture above, it turned out perfectly.

The Circuit diagram is basically a standard Arduino Nano ( I needed access to all of the ADC pins ),

Connections to the RA-02 module is as follows:

RA-02ATMEGA328P
MOSI D11
MISO D12
SCK D13
CE D10
RESET D9
DIO0 D2 ( We need a hardware interrupt pin here )
DIO1 D3 ( Hardware Interrupt, enabled through jumper)
DIO2D5 ( Enabled through a jumper)
DIO3 to DIO 5 Not connected, available on a breakout header
Connections between Ra-02 (SX127x) and ATMEGA328P – Note that all IO Lines are connected through level converters 5v <-> 3.3v

On the ATMEGA328, the following pins are broken out into headers:

D3 can be connected to RA-02 with a jumper
D4
D5 can be connected to RA-02 with a jumper
D6
D7
D8
D13 ( SCK ) Broken out as a 5v logic pin
D12 ( MISO ) 5v logic pin
D11 ( MOSI ) 5v logic pin
A3,A6,A7 ADC Pins ( A0, A1, A2 is used internally to monitor VIN, VCC5v and VCC3v )
A0 = Vcc3v A1 = Vcc5v A2 = VIN
SCLI2C SCL pin OR A5
(I2C pins are at 5v logic levels )
SDA I2C SDA pin OR A4
(I2C pins are at 5v logic levels )
RESET
DTR ( connected through a 100nf Cap to Reset, used for serial uploading firmware )
D0 ( RxD ) UART Rx Pin ( 5v logic )
D1 ( TxD ) UART Tx Pin ( 5v logic )
ATMEGA328P breakout pins. All pins are at 5v logic level

The ATMEGA328P is clocked at 16Mhz through an external crystal.

3v, 5v and GND pins were broken out at two opposite corners of the PCB to allow easy connection of additional devices/ sensors as needed.

Powering the PCB

The PCB can be ordered at PCBWay by clicking here

This image has an empty alt attribute; its file name is Screenshot-from-2021-07-22-18-45-42.png

The MH-CD42 module has the capability to power a load while charging the LiPo battery. This makes it possible to do a few interesting things while supplying power to this particular PCB.

The board can be powered directly from a 5v header pin ( NOT from the DC1 input ). In this mode, a LiPo battery is not required.

5v will be directly supplied to the processor, as well as the 3.3v LDO regulator, allowing the RA-02 module to function as well.

PLEASE NOTE: THERE ARE NO 5v REGULATION – MAKE SURE YOU SUPPLY REGULATED 5v

Installing the MH-CD42 module will add the option of charging a 3.3v 18650 Lipo battery, as well as powering the board at the same time. You need to supply power through the DC plug to make use of this function. This mode is also a miniature uninterruptible power supply, with the 3.3v LiPo battery immediately taking over should the main DC input fail.

NOTE: The MH-CD42 can only accept up to 5.5v MAXIMUM as input!

Software:

The PCB can be used with LoRa libraries from Sandeep Mistry OR JGromes/RadioLib ( If you want to do FSK/OOK or other advanced stuff like LoRa of FHSS)

Sandeep Mistry LoRa Module
JGROMES/RADIOLIB

Both libraries have extensive examples showing how to use them. As our use case will definitely differ, I will not have any example here at this stage. I will add one later after the entire project is completed, with all the relevant modules.

The PCB can be ordered at PCBWay by clicking here

This PCB was manufactured at PCBWAY. The Gerber files and BOM, as well as all the schematics, will soon be available as a shared project on their website. If you would like to have PCBWAY manufacture one of your own, designs, or even this particular PCB, you need to do the following…
1) Click on this link
2) Create an account if you have not already got one of your own.
If you use the link above, you will also instantly receive a $5USD coupon, which you can use on your first or any other order later. (Disclaimer: I will earn a small referral fee from PCBWay. This referral fee will not affect the cost of your order, nor will you pay any part thereof.)
3) Once you have gone to their website, and created an account, or login with your existing account,

4) Click on PCB Instant Quote

5) If you do not have any very special requirements for your PCB, click on Quick-order PCB

6) Click on Add Gerber File, and select your Gerber file(s) from your computer. Most of your PCB details will now be automatically selected, leaving you to only select the solder mask and silk-screen colour, as well as to remove the order number or not. You can of course fine-tune everything exactly as you want as well.

7) You can also select whether you want an SMD stencil, or have the board assembled after manufacturing. Please note that the assembly service, as well as the cost of your components, ARE NOT included in the initial quoted price. ( The quote will update depending on what options you select ).

8) When you are happy with the options that you have selected, you can click on the Save to Cart Button. From here on, you can go to the top of the screen, click on Cart, make any payment(s) or use any coupons that you have in your account.

Then just sit back and wait for your new PCB to be delivered to your door via the shipping company that you have selected during checkout.



Build your own USB-ASP Programmer Clone

Using more ATMEGA based MCU’s in my recent projects, and not using ready-made Arduino boards for any of them, it became necessary to invest in a dedicated ASP programmer to flash the MCU’s. Huge delays on electronics components, with confirmed orders being mysteriously cancelled due to sourcing issues, and the high cost of “original OEM ASP programmers”, send me on a search for a DIY style programmer, like the old “NOPPP” ( NO PARTS PIC Programmer) in the good old days…

Personal computers have changed a lot, and while ATMEGA chips are not PIC’s, I did feel that it could be done. Given the fact that you could also use an Arduino UNO as an ISP, and doing a little more digging, I finally found a very attractive alternative…

Thomas Fischl, on his website,https://www.fischl.de/usbasp, has an open-source hardware project, that seemed to be exactly what I needed. His device is capable of programming 5v devices, at various speeds, including a super slow one. He has also written firmware for the device, and although the last update was in 2011, It still seems to be working well, or at least as far as I can see…

I did however decide to add my own twist to the design and build in a logic level converter, to be able to program 3.3v devices as well…

The modified schematic is below:

Schematic Page 1
Schematic Page 2

I chose to do my own PCB layout, to incorporate the changes listed earlier, namely the logic level conversion from 5v to 3v, as well as adding a 3.3v LDO voltage regulator to provide 3.3v to the target in case it is required.

PCB Topside
PCB Bottom Side
3D Render of the PCB

The PCB is still in transit from the factory, and thus I will update this article later with pictures of the actual device.

Firmware can be flashed using any ASP programmer, Arduino as ISP as well, but with the caveat that you have to use AVRDude from the command line… More on that in the follow-up post… ( I would like to show actual screenshots of the process, and not theory.. ). The links to the firmware are available on https://www.fischl.de/usbasp, courtesy of Thomas Fischl, whom I would like to thank for making this open-source hardware project available freely, as well as for writing and maintaining the firmware.


The PCB can be ordered, or the design files downloaded ( a free download ) from my Projects page at PCBWay soon…

This PCB was manufactured at PCBWAY. The Gerber files and BOM, as well as all the schematics, will soon be available as a shared project on their website. If you would like to have PCBWAY manufacture one of your own, designs, or even this particular PCB, you need to do the following…
1) Click on this link
2) Create an account if you have not already got one of your own.
If you use the link above, you will also instantly receive a $5USD coupon, which you can use on your first or any other order later. (Disclaimer: I will earn a small referral fee from PCBWay. This referral fee will not affect the cost of your order, nor will you pay any part thereof.)
3) Once you have gone to their website, and created an account, or login with your existing account,

4) Click on PCB Instant Quote

5) If you do not have any very special requirements for your PCB, click on Quick-order PCB

6) Click on Add Gerber File, and select your Gerber file(s) from your computer. Most of your PCB details will now be automatically selected, leaving you to only select the solder mask and silk-screen colour, as well as to remove the order number or not. You can of course fine-tune everything exactly as you want as well.

7) You can also select whether you want an SMD stencil, or have the board assembled after manufacturing. Please note that the assembly service, as well as the cost of your components, ARE NOT included in the initial quoted price. ( The quote will update depending on what options you select ).

8) When you are happy with the options that you have selected, you can click on the Save to Cart Button. From here on, you can go to the top of the screen, click on Cart, make any payment(s) or use any coupons that you have in your account.

Then just sit back and wait for your new PCB to be delivered to your door via the shipping company that you have selected during checkout.




Port Extender Card for the MCU-8266-12E

Port Extender Card for the MCU-8266-12E IoT Controller

Introduction

After quite a few experiments, and playing with a lot of ideas, we have finally produced and tested an almost final prototype for the MCU-8266-12E IoT Controller Port Extender Card. While the baseboard already has quite a lot of free GPIO pins for additional sensors and devices, It did however have quite a few issues, namely a lack of sufficient Power outputs, difficulty access to the I2C bus, as well as only 2 relay outputs. Granted that you do have access to unused pins on the PCF8574 Port Extender, We nonetheless decided that an add-on card would definitely make sense to allow this device to really be more useable.

While looking at various ideas for this card, the most flexible seemed to be the APE Protocol device as documented in ESPHome. They used a standard Arduino board for that, but we decided that, after testing it with an Arduino Nano, since it seems to work well, let us just design a dedicated PCB. It also looks much better as well 🙂

Some Features (Aside from being a fully functional Arduino clone as well)

1). Dedicated LDO Regulators for 5v and 3.3v (800mA each), with jumpers to switch them on or off (receive power only from the IoT Motherboard).
2.) Dedicated Logic Level Converter on the I2C Bus ( This is sort of very much needed 🙂 The Atmega 328P-AU is running at 5v on this device, to enable it to run at 16Mhz.. and the ESP8266 on the Motherboard is a 3.3v device..

There are also 3x 3.3V I2C Headers, complete with 3.3v and Ground, as well as a single 5v I2C header
3). 8 Analog Inputs ( While practically you can only use 6 of these if you use I2C )
4). Voltage Divider provided on A0 to measure VIN ( to be safe, we calculated the resistors for 22v)
5). 100R current limiting resistor on A1 and A2, to measure 5v and 3.3v as well…
Analog inputs A0, A1 and A2 can be switched back to normal operation by changing the jumper at J2,J3 or J4 from On to Off.
6). 12 Digital Inputs/Outputs (14 if you use D0 and D1 as well), as well as a Jumper to remove the LED on D13.
7). Full access to the PCF8574 and ESP8266 Pins from the motherboard below.

Pictures of the PCB

Circuit Diagram

ATMega328P-AU Circuit diagram with LDO Regulators, headers and supporting circuitry.
Analog measuring circuitry, level converters and supporting circuitry and headers

Uploading Code to the ATMega328P

Uploading code to the device requires the use of either an ISCP programmer ( Arduino as ISP works well ) or in the case of a pre-boot loaded chip, a USB-to-Serial converter. We did not find it necessary to add a dedicated USB-to-Serial converter onto the PCB. It is quite easy enough to do any flashing with the tools mentioned above.


Make sure that the PCB is not stacked when doing this. ( This will prevent excessive current use of other components when you supply 5v to the PE card.


Procedure to upload using ICSP

During assembly, you are required to solder a single 90-degree bend pin header on the bottom side of the PCB, in the same hole as the board side edge of the RESET push-button. This will serve as the RESET Pin for the ISCP. Other connections are as follows:

H2 Header <- > ICSP Programmer
MOSI (E11~) – MOSI ( or Pin 11 on Arduino as ISP )
MISO (E12 ) – MISO ( or Pin 12 on Arduino as ISP )
SCK (E13) – D13 (or Pin 13 on Arduino as ISP )
RESET – D10 (or Pin 10 on Arduino as ISP )

5v and Ground from Arduino as ISP or ISCP Programmer to any 5v and ground pin on the PE Card

Please note the description above for assembly of the RESET pin header



Procedure to upload using USB-to-Serial converter

H1 Header

E0/Rx <- to Tx of USB-to-Serial converter
E1/Tx -> to Rx of USB-to-Serial converter

H2 Header

DTR <-> to DTR of USB-to-Serial converter [ This connection is needed for successful uploading. Don’t leave it out ]


5v and Ground from the USB-to-Serial converter to any 5v and ground pin on the PE Card


Testing with ESPHome APE protocol and the MCU-8266-12E IoT controller

The following Arduino Sketch needs to be uploaded to the device.
It will allow the device to function as a custom I2C device. Feel free to change the I2C address in the sketch as you choose, but remember to use the same address in your ESPHome YAML configuration file

/*
Ports:
  0 0 .. 13 13
  A0: 14, A1: 15, A2: 16, A3: 17: A4: 18: A5: 19: A6: 20, A7: 21
  port bits: 5 ... 0..32
  0:   0: 00000
  1:   1: 00001
  A7: 21: 10101
*/

#include <Arduino.h>
#include <Wire.h>

//#define DEBUG // remove debug so pin 0 and 1 can be used for IO

#define I2C_ADDRESS 8

void onRequest();
void onReceive(int);

void setup()
{
#ifdef DEBUG
  Serial.begin(115200);
  Serial.println(F("Init "));
#endif

  analogReference(INTERNAL);

  Wire.begin(I2C_ADDRESS);
  Wire.onRequest(onRequest);
  Wire.onReceive(onReceive);

#ifdef DEBUG
  Serial.println(F("Wire ok"));
#endif
}

void loop()
{
  //int temp = analogRead(A1);
  //Serial.println(temp);
}

volatile byte buffer[3];
volatile byte len = 1;

#define DIGITAL_READ(b, pin, mask) \
  if (digitalRead(pin))            \
    buffer[b] |= mask;

void readDigital()
{
  len = 3;
  buffer[0] = 0;
  DIGITAL_READ(0, 0, 1);
  DIGITAL_READ(0, 1, 2);
  DIGITAL_READ(0, 2, 4);
  DIGITAL_READ(0, 3, 8);
  DIGITAL_READ(0, 4, 16);
  DIGITAL_READ(0, 5, 32);
  DIGITAL_READ(0, 6, 64);
  DIGITAL_READ(0, 7, 128);

  buffer[1] = 0;
  DIGITAL_READ(1, 8, 1);
  DIGITAL_READ(1, 9, 2);
  DIGITAL_READ(1, 10, 4);
  DIGITAL_READ(1, 11, 8);
  DIGITAL_READ(1, 12, 16);
  DIGITAL_READ(1, 13, 32);
  DIGITAL_READ(1, A0, 64);
  DIGITAL_READ(1, A1, 128);

  buffer[2] = 0;
  DIGITAL_READ(2, A2, 1);
  DIGITAL_READ(2, A3, 2);

// I2C
//DIGITAL_READ(2, A4, 4);
//DIGITAL_READ(2, A5, 8);

// DIGITAL READ not supports on A3 .. A7
#ifdef DEBUG_READ
  Serial.print(F("Read 3 bytes: "));
  Serial.print(buffer[0]);
  Serial.print(' ');
  Serial.print(buffer[1]);
  Serial.print(' ');
  Serial.println(buffer[2]);

#endif
}
void readAnalog(int pin)
{
  int val = analogRead(A0 + pin);
  len = 2;
  buffer[0] = val & 0xFF;
  buffer[1] = (val >> 8) & 0b11;
#ifdef DEBUG_READ
  Serial.print(F("Read analog pin "));
  Serial.println(pin);
#endif
}

void onRequest()
{
  Wire.write(const_cast<uint8_t *>(buffer), len);
}

#define CMD_DIGITAL_READ 0x0

#define CMD_WRITE_ANALOG 0x2
#define CMD_WRITE_DIGITAL_HIGH 0x3
#define CMD_WRITE_DIGITAL_LOW 0x4

#define CMD_SETUP_PIN_OUTPUT 0x5
#define CMD_SETUP_PIN_INPUT_PULLUP 0x6
#define CMD_SETUP_PIN_INPUT 0x7

// 8 analog registers.. A0 to A7
// A4 and A5 not supported due to I2C
#define CMD_ANALOG_READ_A0 0b1000 // 0x8
// ....
#define CMD_ANALOG_READ_A7 0b1111 // 0xF

#define CMD_SETUP_ANALOG_INTERNAL 0x10
#define CMD_SETUP_ANALOG_DEFAULT 0x11

void onReceive(int numBytes)
{
#ifdef DEBUG_READ
  Serial.print("Received bytes: ");
  Serial.println(numBytes);
#endif
  int cmd = Wire.read();

  switch (cmd)
  {
  case CMD_DIGITAL_READ:
    readDigital();
    break;
  }

  if (cmd >= CMD_ANALOG_READ_A0 && cmd <= CMD_ANALOG_READ_A7)
  {
    readAnalog(cmd & 0b111);
    return;
  }

  int pin = Wire.read();

  switch (cmd)
  {
  case CMD_WRITE_DIGITAL_HIGH:
  case CMD_WRITE_DIGITAL_LOW:
  {
    bool output = cmd == CMD_WRITE_DIGITAL_HIGH;
    digitalWrite(pin, output);
#ifdef DEBUG
    Serial.print(F("Pin "));
    Serial.print(pin);
    Serial.println(output ? F(" HIGH") : F(" LOW"));
#endif
    break;
  }
  case CMD_WRITE_ANALOG:
  {
    int val = Wire.read() & (Wire.read() << 8);
    analogWrite(pin, val);
#ifdef DEBUG
    Serial.print(F("Pin "));
    Serial.print(pin);
    Serial.print(F(" Analog write "));
    Serial.println(val);
#endif
    break;
  }
  case CMD_SETUP_PIN_OUTPUT:
    pinMode(pin, OUTPUT);
#ifdef DEBUG
    Serial.print(F("Pin "));
    Serial.print(pin);
    Serial.println(F(" OUTPUT"));
#endif
    break;
  case CMD_SETUP_PIN_INPUT:
    pinMode(pin, INPUT);
#ifdef DEBUG
    Serial.print(F("Pin "));
    Serial.print(pin);
    Serial.println(F("INPUT"));
#endif
    break;
  case CMD_SETUP_PIN_INPUT_PULLUP:
    pinMode(pin, INPUT_PULLUP);
#ifdef DEBUG
    Serial.print(F("Pin "));
    Serial.print(pin);
    Serial.println(F("INPUT PULLUP"));
#endif
    break;
  case CMD_SETUP_ANALOG_INTERNAL:
    analogReference(INTERNAL);
#ifdef DEBUG
    Serial.println(F("Analog reference INTERNAL"));
#endif
    break;
  case CMD_SETUP_ANALOG_DEFAULT:
    analogReference(DEFAULT);
#ifdef DEBUG
    Serial.println(F("Analog reference DEFAULT"));
#endif
    break;
  }
}

The following C header file needs to be uploaded to your Home Assistant ESPHome folder.

// Must disable logging if using logging in main.cpp or in other custom components for the
//  __c causes a section type conflict with __c thingy
// you can enable logging and use it if you enable this in logger:
/*
logger:
  level: DEBUG
  esp8266_store_log_strings_in_flash: False
  */

//#define APE_LOGGING

// take advantage of LOG_ defines to decide which code to include
#ifdef LOG_BINARY_OUTPUT
#define APE_BINARY_OUTPUT
#endif
#ifdef LOG_BINARY_SENSOR
#define APE_BINARY_SENSOR
#endif
#ifdef LOG_SENSOR
#define APE_SENSOR
#endif

static const char *TAGape = "ape";

#define APE_CMD_DIGITAL_READ 0
#define APE_CMD_WRITE_ANALOG 2
#define APE_CMD_WRITE_DIGITAL_HIGH 3
#define APE_CMD_WRITE_DIGITAL_LOW 4
#define APE_CMD_SETUP_PIN_OUTPUT 5
#define APE_CMD_SETUP_PIN_INPUT_PULLUP 6
#define APE_CMD_SETUP_PIN_INPUT 7
// 8 analog registers.. A0 to A7
// A4 and A5 not supported due to I2C
#define CMD_ANALOG_READ_A0 0b1000 // 0x8
// ....
#define CMD_ANALOG_READ_A7 0b1111 // 0xF

#define CMD_SETUP_ANALOG_INTERNAL 0x10
#define CMD_SETUP_ANALOG_DEFAULT 0x11

#define get_ape(constructor) static_cast<ArduinoPortExpander *>(constructor.get_component(0))

#define ape_binary_output(ape, pin) get_ape(ape)->get_binary_output(pin)
#define ape_binary_sensor(ape, pin) get_ape(ape)->get_binary_sensor(pin)
#define ape_analog_input(ape, pin) get_ape(ape)->get_analog_input(pin)

class ArduinoPortExpander;

using namespace esphome;

#ifdef APE_BINARY_OUTPUT
class ApeBinaryOutput : public output::BinaryOutput
{
public:
  ApeBinaryOutput(ArduinoPortExpander *parent, uint8_t pin)
  {
    this->parent_ = parent;
    this->pin_ = pin;
  }
  void write_state(bool state) override;
  uint8_t get_pin() { return this->pin_; }

protected:
  ArduinoPortExpander *parent_;
  uint8_t pin_;
  // Pins are setup as output after the state is written, Arduino has no open drain outputs, after setting an output it will either sink or source thus activating outputs writen to false during a flick.
  bool setup_{true};
  bool state_{false};

  friend class ArduinoPortExpander;
};
#endif

#ifdef APE_BINARY_SENSOR
class ApeBinarySensor : public binary_sensor::BinarySensor
{
public:
  ApeBinarySensor(ArduinoPortExpander *parent, uint8_t pin)
  {
    this->pin_ = pin;
  }
  uint8_t get_pin() { return this->pin_; }

protected:
  uint8_t pin_;
};
#endif

#ifdef APE_SENSOR
class ApeAnalogInput : public sensor::Sensor
{
public:
  ApeAnalogInput(ArduinoPortExpander *parent, uint8_t pin)
  {
    this->pin_ = pin;
  }
  uint8_t get_pin() { return this->pin_; }

protected:
  uint8_t pin_;
};
#endif

class ArduinoPortExpander : public Component, public I2CDevice
{
public:
  ArduinoPortExpander(I2CBus *bus, uint8_t address, bool vref_default = false)
  {
    set_i2c_address(address);
    set_i2c_bus(bus);
    this->vref_default_ = vref_default;
  }
  void setup() override
  {
#ifdef APE_LOGGING
    ESP_LOGCONFIG(TAGape, "Setting up ArduinoPortExpander at %#02x ...", address_);
#endif

    /* We cannot setup as usual as arduino boots later than esp8266
            Poll i2c bus for our Arduino for a n seconds instead of failing fast,
            also this is important as pin setup (INPUT_PULLUP, OUTPUT it's done once)
        */
    this->configure_timeout_ = millis() + 5000;
  }
  void loop() override
  {
    if (millis() < this->configure_timeout_)
    {
      bool try_configure = millis() % 100 > 50;
      if (try_configure == this->configure_)
        return;
      this->configure_ = try_configure;

      if (ERROR_OK == this->read_register(APE_CMD_DIGITAL_READ, const_cast<uint8_t *>(this->read_buffer_), 3))
      {
#ifdef APE_LOGGING
        ESP_LOGCONFIG(TAGape, "ArduinoPortExpander found at %#02x", address_);
#endif
        delay(10);
        if (this->vref_default_)
        {
          this->write_register(CMD_SETUP_ANALOG_DEFAULT, nullptr, 0); // 0: unused
        }

        // Config success
        this->configure_timeout_ = 0;
        this->status_clear_error();
#ifdef APE_BINARY_SENSOR
        for (ApeBinarySensor *pin : this->input_pins_)
        {
          App.feed_wdt();
          uint8_t pinNo = pin->get_pin();
#ifdef APE_LOGGING
          ESP_LOGCONFIG(TAGape, "Setup input pin %d", pinNo);
#endif
          this->write_register(APE_CMD_SETUP_PIN_INPUT_PULLUP, &pinNo, 1);
          delay(20);
        }
#endif
#ifdef APE_BINARY_OUTPUT
        for (ApeBinaryOutput *output : this->output_pins_)
        {
          if (!output->setup_)
          { // this output has a valid value already
            this->write_state(output->pin_, output->state_, true);
            App.feed_wdt();
            delay(20);
          }
        }
#endif
#ifdef APE_SENSOR
        for (ApeAnalogInput *sensor : this->analog_pins_)
        {
          App.feed_wdt();
          uint8_t pinNo = sensor->get_pin();
#ifdef APE_LOGGING
          ESP_LOGCONFIG(TAGape, "Setup analog input pin %d", pinNo);
#endif
          this->write_register(APE_CMD_SETUP_PIN_INPUT, &pinNo, 1);
          delay(20);
        }
#endif
        return;
      }
      // Still not answering
      return;
    }
    if (this->configure_timeout_ != 0 && millis() > this->configure_timeout_)
    {
#ifdef APE_LOGGING
      ESP_LOGE(TAGape, "ArduinoPortExpander NOT found at %#02x", address_);
#endif
      this->mark_failed();
      return;
    }

#ifdef APE_BINARY_SENSOR
    if (ERROR_OK != this->read_register(APE_CMD_DIGITAL_READ, const_cast<uint8_t *>(this->read_buffer_), 3))
    {
#ifdef APE_LOGGING
      ESP_LOGE(TAGape, "Error reading. Reconfiguring pending.");
#endif
      this->status_set_error();
      this->configure_timeout_ = millis() + 5000;
      return;
    }
    for (ApeBinarySensor *pin : this->input_pins_)
    {
      uint8_t pinNo = pin->get_pin();

      uint8_t bit = pinNo % 8;
      uint8_t value = pinNo < 8 ? this->read_buffer_[0] : pinNo < 16 ? this->read_buffer_[1] : this->read_buffer_[2];
      bool ret = value & (1 << bit);
      if (this->initial_state_)
        pin->publish_initial_state(ret);
      else
        pin->publish_state(ret);
    }
#endif
#ifdef APE_SENSOR
    for (ApeAnalogInput *pin : this->analog_pins_)
    {
      uint8_t pinNo = pin->get_pin();
      pin->publish_state(analogRead(pinNo));
    }
#endif
    this->initial_state_ = false;
  }

#ifdef APE_SENSOR
  uint16_t analogRead(uint8_t pin)
  {
    bool ok = (ERROR_OK == this->read_register((uint8_t)(CMD_ANALOG_READ_A0 + pin), const_cast<uint8_t *>(this->read_buffer_), 2));
#ifdef APE_LOGGING
    ESP_LOGVV(TAGape, "analog read pin: %d ok: %d byte0: %d byte1: %d", pin, ok, this->read_buffer_[0], this->read_buffer_[1]);
#endif
    uint16_t value = this->read_buffer_[0] | ((uint16_t)this->read_buffer_[1] << 8);
    return value;
  }
#endif

#ifdef APE_BINARY_OUTPUT
  output::BinaryOutput *get_binary_output(uint8_t pin)
  {
    ApeBinaryOutput *output = new ApeBinaryOutput(this, pin);
    output_pins_.push_back(output);
    return output;
  }
#endif
#ifdef APE_BINARY_SENSOR
  binary_sensor::BinarySensor *get_binary_sensor(uint8_t pin)
  {
    ApeBinarySensor *binarySensor = new ApeBinarySensor(this, pin);
    input_pins_.push_back(binarySensor);
    return binarySensor;
  }
#endif
#ifdef APE_SENSOR
  sensor::Sensor *get_analog_input(uint8_t pin)
  {
    ApeAnalogInput *input = new ApeAnalogInput(this, pin);
    analog_pins_.push_back(input);
    return input;
  }
#endif
  void write_state(uint8_t pin, bool state, bool setup = false)
  {
    if (this->configure_timeout_ != 0)
      return;
#ifdef APE_LOGGING
    ESP_LOGD(TAGape, "Writing %d to pin %d", state, pin);
#endif
    this->write_register(state ? APE_CMD_WRITE_DIGITAL_HIGH : APE_CMD_WRITE_DIGITAL_LOW, &pin, 1);
    if (setup)
    {
      App.feed_wdt();
      delay(20);
#ifdef APE_LOGGING
      ESP_LOGI(TAGape, "Setup output pin %d", pin);
#endif
      this->write_register(APE_CMD_SETUP_PIN_OUTPUT, &pin, 1);
    }
  }

protected:
  bool configure_{true};
  bool initial_state_{true};
  uint8_t read_buffer_[3]{0, 0, 0};
  unsigned long configure_timeout_{5000};
  bool vref_default_{false};

#ifdef APE_BINARY_OUTPUT
  std::vector<ApeBinaryOutput *> output_pins_;
#endif
#ifdef APE_BINARY_SENSOR
  std::vector<ApeBinarySensor *> input_pins_;
#endif
#ifdef APE_SENSOR
  std::vector<ApeAnalogInput *> analog_pins_;
#endif
};

#ifdef APE_BINARY_OUTPUT
void ApeBinaryOutput::write_state(bool state)
{
  this->state_ = state;
  this->parent_->write_state(this->pin_, state, this->setup_);
  this->setup_ = false;
}
#endif

The file should be named “arduino_port_expander.h”

Make the following changes to your ESPHome YAML configuration file for the MCU-8266-12E device

esphome:
  name: mcu-8266-12e-01
  platform: ESP8266
  board: nodemcuv2
  includes:
      - arduino_port_expander.h
# Note the include file - This loads the APE Header

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:
  password: "<your password will be different - dont change it>"

wifi:
  ssid: <your ssid>
  password: <your password>

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "MCU-8266-Hotspot"
    password: "password"

captive_portal:

i2c:
# PCB Prototype
  sda: GPIO5
  scl: GPIO4
# PCB Rev 1.5 or higher, comment the above 2 lines
# and uncomment
#sda: GPIO4
#scl: GPIO5
#################### - IMPORTANT ###########
  scan: true
  id: i2c_bus_a
  
pcf8574:
  - id: 'pcf8574_hub'
    address: 0x22 # Set at 0x22, feel free to change to your liking, Remember to set the chip to the address you choose as well
    pcf8575: false

time:
  - platform: sntp
    id: ha_time
    timezone: "Etc/GMT+7"

status_led:
  pin:
    number: GPIO16
    inverted: true

#Define the APE as a custom component, taking care to ensure that:
#1). The I2C Bust ID is the same as the one you have defined in the I2C: Section
#2). The address of the APE is the same as the one you set in the sketch
    
custom_component:
  - id: ape
    lambda: |-
      auto ape_component = new ArduinoPortExpander(i2c_bus_a, 0x08,true);
      return {ape_component};
    

sensor:

  - platform: custom
    lambda: |-
      return {ape_analog_input(ape, 0),  // 1 = A1
             ape_analog_input(ape, 1),
             ape_analog_input(ape, 2)};
   
#We define 3 analog inputs (A0,A1,A2) to monitor voltages on the card
#Note that you MUST define them in the sensors section below as well AND
#THAT THEY MUST BE IN THE SAME SEQUENCE THAT YOU DEFINED THEM IN ABOVE HERE
#
#ALSO NOTE THAT YOU CAN "NOT" use A4 and A5, as they are used for I2C !
#
# As an example, of adding another 3 analog inputs, your definition above will change to:
#
#    return {ape_analog_input(ape, 0),
#           ape_analog_input(ape, 1),
#           ape_analog_input(ape, 2),
#           ape_analog_input(ape, 3),
#           ape_analog_input(ape, 6),
#           ape_analog_input(ape, 7)};
#
#
# Now define the sensors connected to these analogs below:

             
    sensors:
      - name: "PE Card VIN"
        id: analog_a0
        device_class: "voltage"
        unit_of_measurement: "v"
        accuracy_decimals: 2
        filters:
          - lambda: return x * (22.00/1023.0);
          - throttle: 60s

# We use a lambda to scale the value of VIN - Our Voltage divider was designed around 22 volt
# thus we need 22 volt here in the calculation as well to make it accurate
#
      - name: "PE Card 5v"
        id: analog_a1
        device_class: "voltage"
        unit_of_measurement: "v"
        accuracy_decimals: 2
        filters:
          - lambda: return x * (5.02/1023.0); 
          - throttle: 60s
      - name: "PE Card 3v"
        id: analog_a2
        device_class: "voltage"
        unit_of_measurement: "v"
        accuracy_decimals: 2
        filters:
          - lambda: return x * (5.02/1023.0);
          - throttle: 60s
 
# The ATMega328P 's Analog Reference is set to 5v internally, thus we need to also scale the 
# 3v input with a maximum of 5v ... 

# In case you enabled the other 3 Analog Inputs above, you need to add the following
#
#    - name: "Analog 3"
#      id: analog_a3
#      filters:
#        - throttle: 60s
#    - name: "Analog 6"
#      id: analog_a6
#      filters:
#        - throttle: 60s
#    - name: "Analog 7"
#      id: analog_a7
#      filters:
#        - throttle: 60s
#
#
             
             
             
  - platform: adc
    pin: VCC
    name: "ESP8266 Chip Voltage"
    id: mcu_voltage
    unit_of_measurement: "V"
    device_class: "voltage"
    accuracy_decimals: 2
    update_interval: 60s
    
  - platform: wifi_signal
    name: "WiFi Signal Sensor"
    id: wifi_strength
    device_class: "signal_strength"
    unit_of_measurement: "dBm"
    update_interval: 240s
    

#Digital outputs function the same

output:
- platform: custom
  type: binary
  lambda: |-
    return {ape_binary_output(ape, 2),
            ape_binary_output(ape, 3),
            ape_binary_output(ape, 4),
            ape_binary_output(ape, 5),
            ape_binary_output(ape, 6),
            ape_binary_output(ape, 7)};
  outputs:
    - id: ape_output_p2
      inverted: false
    - id: ape_output_p3
      inverted: false
    - id: ape_output_p4
      inverted: false
    - id: ape_output_p5
      inverted: false
    - id: ape_output_p6
      inverted: false
    - id: ape_output_p7
      inverted: false
      
- platform: gpio
  id: relay_1
  pin:
    pcf8574: pcf8574_hub
    number: 0
    mode: OUTPUT
    inverted: true
- platform: gpio
  id: relay_2
  pin:
    pcf8574: pcf8574_hub
    number: 1
    mode: OUTPUT
    inverted: true
- platform: gpio
  id: led_status_1
  pin:
    pcf8574: pcf8574_hub
    number: 2
    mode: OUTPUT
    inverted: true
- platform: gpio
  id: led_status_2
  pin:
    pcf8574: pcf8574_hub
    number: 3
    mode: OUTPUT
    inverted: true
      
binary_sensor:
  - platform: gpio
    id: push_button_1
    name: 'Relay1 Pushbutton'
    device_class: ''
    pin: 
      pcf8574: pcf8574_hub
      number: 4
      mode: INPUT
      inverted: true
    on_press:
      then:
        - switch.toggle: switch_relay1
        
  - platform: gpio
    id: push_button_2
    name: 'Relay2 Pushbutton'
    device_class: ''
    pin:
      pcf8574: pcf8574_hub
      number: 5
      mode: INPUT
      inverted: true
    on_press:
      #min_length: 50ms
      #max_length: 500ms
      then:
        - switch.toggle: switch_relay2
    filters:
        - delayed_on_off: 50ms
      
  

switch:
  - platform: output
    id: switch_relay1
    name: "Relay No. 1 (#0)"
    output: relay_1
    on_turn_on:
      - output.turn_on: led_status_1
    on_turn_off:
      - output.turn_off: led_status_1
      
  - platform: output
    id: switch_relay2
    name: "Relay No. 2 (#1)"
    output: relay_2
    on_turn_on:
      - output.turn_on: led_status_2
    on_turn_off:
      - output.turn_off: led_status_2
  - platform: restart
    id: reboot_switch
    name: "Reboot Me"

Detailed information on the APE is available here

Manufacturing the PCB

This PCB was manufactured at PCBWAY. The Gerber files and BOM, as well as all the schematics, will soon be available as a shared project on their website. If you would like to have PCBWAY manufacture one of your own, designs, or even this particular PCB, you need to do the following…
1) Click on this link
2) Create an account if you have not already got one of your own.
If you use the link above, you will also instantly receive a $5USD coupon, which you can use on your first or any other order later. (Disclaimer: I will earn a small referral fee from PCBWay. This referral fee will not affect the cost of your order, nor will you pay any part thereof.)
3) Once you have gone to their website, and created an account, or login with your existing account,

4) Click on PCB Instant Quote

5) If you do not have any very special requirements for your PCB, click on Quick-order PCB

6) Click on Add Gerber File, and select your Gerber file(s) from your computer. Most of your PCB details will now be automatically selected, leaving you to only select the solder mask and silk-screen colour, as well as to remove the order number or not. You can of course fine-tune everything exactly as you want as well.

7) You can also select whether you want an SMD stencil, or have the board assembled after manufacturing. Please note that the assembly service, as well as the cost of your components, ARE NOT included in the initial quoted price. ( The quote will update depending on what options you select ).

8) When you are happy with the options that you have selected, you can click on the Save to Cart Button. From here on, you can go to the top of the screen, click on Cart, make any payment(s) or use any coupons that you have in your account.

Then just sit back and wait for your new PCB to be delivered to your door via the shipping company that you have selected during checkout.

ATMega328P prototype add-on card for use with the MCU-8266-12E IoT controller.

In August of 2021, MakerIoT2020 released the MCU-8266-12E IoT Controller PCB, (part 1 is available here in case you missed that). Shortly after that, we started working on an expansion add-on card, that would work with the APE (Arduino Port Expander) protocol in ESPHome.

While I could have used a standard Arduino board for this, and in fact, I have done so during many of the testing stages,
I decided to design a custom PCB specifically for this task, in order to achieve two specific things…

1). The standard Arduino Board comes in either a 5v logic or 3v logic device. While this is perfect for many projects,
it is still sometimes required to use a logic level converter with some sensors and devices. LORA is a good example of that. As I really dislike using a breadboard, due to their inherent unreliable connections and the ever-present mess of wires going everywhere, I wanted an Arduino or ATMEGA328 based device that already has a level converter built-in.

As I could not find anything like that for sale, I decided to build my own, as you will see shortly.

2). I wanted to start moving away from using the Arduino IDE as much as possible. While the Arduino IDE is great for most tasks, It does lack in a few areas. I thus want to slowly ease myself back into using AVR C, and that requires a board that can be flashed via ICSP. ( yes, yes, you can flash an Arduino with ICSP as well. ) In the case of the planned expansion card, it would basically be a device that is flashed once and then left alone. Serial flashing would be quite unnecessary on there anyway.

The other reason, still part of point 2, is that it seems like everyone else is having all sorts of problems with fuses on the ATMega328 on custom boards etc… I wanted to see if that is really the case or not…

The PCB should also be useable as a standard “Arduino” type device to assist in prototyping and development.

ATMega328P Custom PCB – as a prototype add-on card to the MCU-8266-12E IoT controller

a Quick description of the PCB:

Standard Arduino type headers and pins are provided, with pin labels as for the Arduino Nano.
This gives us:

ATMega 328P MCU running at 16Mhz
12 Digital IO (D2 to D13) [ 14 if we use D1 and D2 as well ]
8 Analog Inputs (A0 to A7) [ A4 and A5 are used for I2C ]
ICSP header for uploading code
USB Port with CH340G for Arduino style serial flashing [This will be removed on the next version]
A Dedicated LDO 3.3v Voltage regulator, with a selectable input source (5v from USB, or directly from VIN – for high current use applications – MAX of 800mA)

An 8 Channel Bi-Directional Logic Level Converter, for now, the converter is fixed at bi-directional 3v to 5v conversion.
Additional 5v (x4), 3v (x4) and Ground pins (x8), as well as 2 general use bus connections (G1, G2) which I added for use with I2C

Led’s are provided on 5v, 3v, Serial Rx, Tx, as well as on pin D13.

Dimensions: 86mm x 51mm

Assembly – During Reflow on a hotplate.
During Reflow

Manufacturing the PCB

This PCB was manufactured at PCBWAY. The Gerber files and BOM, as well as all the schematics, will soon be available as a shared project on their website. If you would like to have PCBWAY manufacture one of your own, designs, or even this particular PCB, you need to do the following…
1) Click on this link
2) Create an account if you have not already got one of your own.
If you use the link above, you will also instantly receive a $5USD coupon, which you can use on your first or any other order later. (Disclaimer: I will earn a small referral fee from PCBWay. This referral fee will not affect the cost of your order, nor will you pay any part thereof.)
3) Once you have gone to their website, and created an account, or login with your existing account,

4) Click on PCB Instant Quote

5) If you do not have any very special requirements for your PCB, click on Quick-order PCB

6) Click on Add Gerber File, and select your Gerber file(s) from your computer. Most of your PCB details will now be automatically selected, leaving you to only select the solder mask and silk-screen colour, as well as to remove the order number or not. You can of course fine-tune everything exactly as you want as well.

7) You can also select whether you want an SMD stencil, or have the board assembled after manufacturing. Please note that the assembly service, as well as the cost of your components, ARE NOT included in the initial quoted price. ( The quote will update depending on what options you select ).

8) When you are happy with the options that you have selected, you can click on the Save to Cart Button. From here on, you can go to the top of the screen, click on Cart, make any payment(s) or use any coupons that you have in your account.

Then just sit back and wait for your new PCB to be delivered to your door via the shipping company that you have selected during checkout.

Conclusion

In conclusion, the PCB works quite well, with no issues with flashing the ATMEGA328P with an ICSP programmer from the Arduino IDE, as well as via USB from the Arduino IDE.

The level converter works as expected, successfully translating bidirectional signals on I2C and SPI to and from 3v and 5v devices.

In the next stage, we will focus on the stock APE protocol sketch, as provided by ESPHome, and then, once that is working perfectly, modify it to suit our needs.


Design and build an ESP8266 IoT Controller, Part 2

In my previous post, available here, I have shown you the initial stages of designing and developing our own IoT controller. Today, while the PCB has just arrived from PCBWay, and while we wait for the rest of the components to arrive, we will continue with the software development of the device. I will also show you the beautiful PCB that I got from PCBWay!

For today’s post, we will focus on configuring ESPHome, get it to work together with Home Assistant, as well as have a look at those PCB’s!

Table of Contents
Configuring ESPHome
The YAML configuration file
Updating the prototype device OTA
Integrating it all with Home Assistant
Taking a look at the PCB’s
Conclusion of Part 2

Configuring ESPHome

In the last post, we left off here…

After Flashing ESPHome to the device

Our next step will be to write the YAML configuration file…

YAML configuration

ESPHome uses the YAML language to define IO and automation. These files are then parsed, converted into C/C++ and compiled. The resulting Binary File is the uploaded OTA to the device via WiFi.

We will still be using our Virtual Machine Image of Home Assistant, running inside VirtualBox on our PC.
Go ahead and start that, and then open Home Assistant in your Web Browser. Then click on ESPHome.
You will see a screen similar to the one above.

While we are at this point, I believe it is a good time to clarify why we do the development in a virtual PC, and not directly on our Home Assistant instance running on a Raspberry Pi. The reason is actually very simple. Home Assistant seems to have issues with completely removing unused entities. This is not a problem, but you could potentially end up with a lot of stale entries in your production Home Assistant Server. The VM solution allows us to test everything away from the actual server, and then, when we are done, recreate the working device on the actual server. That way, there is almost no chance of damaging your existing Home Assistant Server, which you may already have spent some time on to set up just the way you want it…

Continue by clicking on the EDIT link, in the iot-con-prototype device that we created in the last tutorial.
You will see a file similar to this …

esphome:
  name: iot-con-prototype
  platform: ESP8266
  board: nodemcuv2

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:
  password: "3f132fc270315361e4d2393a50c2bac5"

wifi:
  ssid: "<your ssid here>"
  password: "<your password here>"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Iot-Con-Prototype"
    password: "4uSytyPTx1TO"

captive_portal:

This is the default configuration file generated by ESPHome. We will build on this file… Note that your OTA Password will be different. DO NOT CHANGE OR MODIFY IT IN ANY WAY!

When we edit YAML, indentations are extremely important, so try to follow exactly what I am doing.

Our IoT Controller uses I2C to communicate with the PCF8574 chip. Let us assume that you have set your address to 0x22h by using the dip switch on the PCB, you could of course set any other address, just make sure that you know what it is.

We now need to tell ESPHome that we have an I2C Bus and that it needs to scan this bus for devices. That way, we can see in the logs if it actually detects our device or not.

Add the following 5 lines to your file:

i2c:
  sda: GPIO4
  scl: GPIO5
  scan: true
  id: i2c_bus_a

They mean that we will have an I2C bus with SDA on GPIO4, and SCL on GPIO5.
The bus will have an ID of i2c_bus_a and the bus should be scanned

The next section will be the actual PCF8574 Io Expander
Add the following lines to the file:

pcf8574:
  - id: 'pcf8574_hub'
    address: 0x22
    pcf8575: false

We give this device the id of pcf8574_hub and specify its address as 0x22. We also NEED to specify that it is NOT the 16 port pcf8575 variant.

Now we can start configuring our outputs. for our purposes, we will have two relay outputs, as well as two status LEDs. In the actual circuit, the pcf8574 will sink the pins connected to these, as the chip can sink more current than it can safely source. Refer to the relay driver schematic for more information on that.

Go ahead, and add the following to your file.

output:
 - platform: gpio
   id: relay_1
   pin: 
      pcf8574: pcf8574_hub
      number: 0
      mode: OUTPUT
      inverted: true
 - platform: gpio
   id: relay_2
   pin: 
      pcf8574: pcf8574_hub
      number: 1
      mode: OUTPUT
      inverted: true
 - platform: gpio
   id: led_status_1
   pin: 
      pcf8574: pcf8574_hub
      number: 2
      mode: OUTPUT
      inverted: true
 - platform: gpio
   id: led_status_2
   pin: 
      pcf8574: pcf8574_hub
      number: 3
      mode: OUTPUT
      inverted: true

The syntax should be straightforward, every output will have a platform, in this case, gpio.
Then we will need a unique ID, let us say relay_1
Now, we need to specify the physical pin to use. Now here, you could also specify a native pin on the actual NodeMCU device that we are using for testing, but we will specify a pin on the pcf8574 instead.
This is done with the pin: directive
pin:
pcf8574: pcf8574_hub // This tells the parser to use the device at address 0x22 that we specified before.
number: 0 // use GPIO 0
mode: OUTPUT // this can be either INPUT or OUTPUT, for our case, it should be OUTPUT.
inverted: true // Invert the logic of the pin.

Once again, it is needed, as we are sinking current into the pin, and the circuit was designed that way…
You can however use it noninverted if you really want, it will just look a bit odd on Home Assistant, if your On state, actually meant Off

The next section will be called binary inputs and may seem a bit confusing at first. But, trust me, it is not. This is just the way that we define our physical push buttons on the device. We need those, in the event that our Home Assistant Server is offline, or when we need to physically press a button on the device.

What? Why? Are we not building an IoT Controller? Why should we press any physical buttons on it?
Well, the answer to that is quite obvious. There will definitely be times that you want to control an attached device by pressing a physical button. It does not make sense to scurry around, swiping through apps on your smartphone, while you are standing right next to the device in question. To leave out this basic functionality, would be plain silly, and in my view, bad engineering.

binary_sensor:
  -  platform: gpio
     id: push_button_1
     name: 'Relay1 Pushbutton'
     device_class: ''
     pin:
        pcf8574:  pcf8574_hub
        number: 4
        mode: INPUT
        inverted: true
     on_press:
      then:
        - switch.toggle: switch_relay1
     filters:
       -  delayed_on_off: 50ms
       
  -  platform: gpio
     id: push_button_2
     name: 'Relay2 Pushbutton'
     device_class: ''
     pin:
        pcf8574:  pcf8574_hub
        number: 5
        mode: INPUT
        inverted: true
     on_press:
      then:
        - switch.toggle: switch_relay2
     filters:
       -  delayed_on_off: 50ms

The syntax is almost the same as the outputs above, but we are also adding automation:
We want the relay to be toggled each time the button is pressed. This will allow us to use only one button per relay to switch it on or off.
We are also adding a filter, in this case, a bit of debouncing, of 50ms. This will prevent the chattering of the contacts on the switch from generating more than one event for each button press.

The final section of the file will be a switch: section. This will allow you to control the relays from inside Home Assistant. ( The outputs are considered an internal to ESPHome function, and will thus not be exposed to Home Assistant.
Add the following lines to the file

switch:
  - platform: output 
    id: switch_relay1
    name: "Relay No. 1 (#0)"
    output: relay_1
    on_turn_on:
    - output.turn_on: led_status_1
    on_turn_off:
    - output.turn_off: led_status_1
  - platform: output 
    id: switch_relay2
    name: "Relay No. 2 (#1)"
    output: relay_2
    on_turn_on:
    - output.turn_on: led_status_2
    on_turn_off:
    - output.turn_off: led_status_2

You can see that the syntax is once again very easy to understand. We also specify that the status LED for the relevant relay channel be switched on or off with the relay. This also happens when you press the physical switch.
The name element will be the Name of the Output that will be displayed in Home Assistant. You can change it to your liking here, or you can also change it inside Home Assistant itself.

The completed file should now look like this. NOTE that if you decide to copy-paste the file from here, as I would recommend, you should only copy from the i2c section to the end of the file. Do not copy the WiFi and other sections. Your own file already has your own settings.

esphome:
  name: iot-con-prototype
  platform: ESP8266
  board: nodemcuv2

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:
  password: "3f132fc270315361e4d2393a50c2bac5"

wifi:
  ssid: "<your ssid here>"
  password: "<your password here>"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Iot-Con-Prototype"
    password: "4uSytyPTx1TO"

captive_portal:
#DO NOT COPY ANYTHING ABOVE THIS LINE -------- 
i2c:
  sda: GPIO4
  scl: GPIO5
  scan: true
  id: i2c_bus_a
  
pcf8574:
  - id: 'pcf8574_hub'
    address: 0x22
    pcf8575: false


output:
 - platform: gpio
   id: relay_1
   pin: 
      pcf8574: pcf8574_hub
      number: 0
      mode: OUTPUT
      inverted: true
 - platform: gpio
   id: relay_2
   pin: 
      pcf8574: pcf8574_hub
      number: 1
      mode: OUTPUT
      inverted: true
 - platform: gpio
   id: led_status_1
   pin: 
      pcf8574: pcf8574_hub
      number: 2
      mode: OUTPUT
      inverted: true
 - platform: gpio
   id: led_status_2
   pin: 
      pcf8574: pcf8574_hub
      number: 3
      mode: OUTPUT
      inverted: true

binary_sensor:
  -  platform: gpio
     id: push_button_1
     name: 'Relay1 Pushbutton'
     device_class: ''
     pin:
        pcf8574:  pcf8574_hub
        number: 4
        mode: INPUT
        inverted: true
     on_press:
      then:
        - switch.toggle: switch_relay1
     filters:
       -  delayed_on_off: 50ms
       
  -  platform: gpio
     id: push_button_2
     name: 'Relay2 Pushbutton'
     device_class: ''
     pin:
        pcf8574:  pcf8574_hub
        number: 5
        mode: INPUT
        inverted: true
     on_press:
      #min_length: 50ms
      #max_length: 500ms
      then:
        - switch.toggle: switch_relay2
     filters:
       -  delayed_on_off: 50ms    
switch:
  - platform: output 
    id: switch_relay1
    name: "Relay No. 1 (#0)"
    output: relay_1
    on_turn_on:
    - output.turn_on: led_status_1
    on_turn_off:
    - output.turn_off: led_status_1
  - platform: output 
    id: switch_relay2
    name: "Relay No. 2 (#1)"
    output: relay_2
    on_turn_on:
    - output.turn_on: led_status_2
    on_turn_off:
    - output.turn_off: led_status_2
  

Well done, That was quite easy.
Copy and paste the file into the edit screen that you have opened in ESPHome.

If everything went well. you should see an option to save or install as in the picture below.

YAML configuration file. ESPHome

If you can see both Save and Install, it means that your file syntax is correct. If not, go back and carefully check your indentations, as well as the spelling of the commands. It should not be a problem if you copied it from above…

Now click on save. You will get a confirmation.
Make sure that the prototype is connected to the USB port of the computer, or to another power source. Wait a few seconds to make sure that it is connected to the network, and then click Install.

ESPHome will now parse your YAML file, generate the needed code, and compile it. After that, it will upload the new firmware to your device. Wait until you can see the device restart and connect to the network in the log window.

The Firmware is compiled and the uploaded OTA
The device had booted, note that the PCF8574 has been detected and that we can see our configuration data (in purple text)

Configuring Home Assistant

We can now start to add this all to Home Assistant. It is also very likely that you will be prompted to add the device by Home Assistant itself. If this did not happen, or like with my testing image on the VM, you have a lot of old stuff floating around as well, we can do it manually …

Go to the Configuration Menu, and click on Integrations.
Click on the esp8266-nodemcu option on ESPHome
Click on the entities, next to the device …
The available entities ( selected with a blue checkmark ). Entities with a red warning are stale, meaning old, unavailable or offline… You can remove them, but they tend to show up again on their own…

We can now select the two relays, as well as the Pushbuttons. Then click on Enable selected.

When you now go back to the Home Assistant Home screen, you will see your device with its controls, on the screen. You can now control the device from Home Assistant. You will note that the two push-buttons are also present in Home Assistant, but, they are only indicators. This means that while they will change state briefly when you press the physical switches, they can not be controlled directly from Home Assistant. They can however be used in automation, or at least I believe so. The debouncing times are however rather short, so I would recommend that you base any automation on the actual relay states instead.

A sneak preview of the PCB

As promised above, I will now give you a quick preview of the PCB’s. They were manufactured by PCBWay

I am once again awed by the precision of these bords. They are absolutely flawless and exactly true to my design.
The irregular isolation cut-outs are exactly to speck and size, the silkscreen is crisp and not blurred. All component footprints are correct, and there seem to be no shorts or open circuits on initial testing with a multimeter.

You can get your own version of these for only $USD 5.00 excluding shipping from here I believe you will be just as impressed as I am.

So let us look at these boards…

My biggest frustration now will be to wait for the rest of the components to arrive. I already have some of them in stock, and can repurpose some others, but I think that I will rather wait for everything to arrive so that I don’t have to
assemble them in stages. I plan to assemble 2 of the 10 boards and make some of the remaining 8 available to interested people. Contact me on messenger if you are interested.

Conclusion

This concludes part 2 of this series. In part 3, we will look at the assembled PCB’s, as well as take a more detailed look at using them together with Home Assistant. I will also attempt to do the Tasmota integration, this will involve compiling my own special version of Tasmota.

I am also planning to release the assembly process as a series of pictures or maybe a video on Patreon.

Become a Patron!

If you like what I do, I want to ask you to consider becoming a Patron. With some assistance from generous people like yourself, I can create some more interesting projects.

Thank you, and Good-Bye until next time.