<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Graham Trott</title>
    <description>The latest articles on DEV Community by Graham Trott (@gtanyware).</description>
    <link>https://dev.to/gtanyware</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F124120%2F816fc938-f821-4f06-ba89-2313994a95dc.jpeg</url>
      <title>DEV Community: Graham Trott</title>
      <link>https://dev.to/gtanyware</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gtanyware"/>
    <language>en</language>
    <item>
      <title>AI writes the code, humans review it — review is the coming skill</title>
      <dc:creator>Graham Trott</dc:creator>
      <pubDate>Sun, 09 Aug 2026 14:31:08 +0000</pubDate>
      <link>https://dev.to/gtanyware/ai-writes-the-code-humans-review-it-review-is-the-coming-skill-330i</link>
      <guid>https://dev.to/gtanyware/ai-writes-the-code-humans-review-it-review-is-the-coming-skill-330i</guid>
      <description>&lt;p&gt;Many articles on dev.to are about coding, but we are rapidly entering a world where this job is done largely by AI and where humans review the code produced. This has consequences for which skills matter, so I want to focus here on the relatively neglected but increasingly vital skill of code review, and later describe a working example — Doclets — a small tool you can run, read and adapt.&lt;/p&gt;

&lt;p&gt;This is a personal view, and it is meant to be argued with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recognition is easier than writing
&lt;/h2&gt;

&lt;p&gt;As time goes by, you are ever more unlikely to be asked to write code — that job is fast becoming one performed almost exclusively by AI. Your job will be first to prompt the AI, and then to review the code presented in the language chosen for the project. Review relies not on writing but on reading and recognition, and it becomes increasingly effective the closer the language comes to a human language. Most coding tools still focus on writing code, assuming human programmers will continue to do it. Tools that put readability first are the exception — and AllSpeak is a deliberate one.&lt;/p&gt;

&lt;p&gt;AllSpeak is a coding tool closely resembling natural human languages. It can do most of the things normally done by Python or JavaScript, but its focus is on keeping code readable by humans. Most things can be described clearly using natural language, so it makes sense for AI to be asked to code using constrained, unambiguous dialects of the languages used by the humans in charge.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Assertion&lt;/strong&gt;&lt;br&gt;
If a thing can be described in English, it should, if at all possible, be coded in something resembling English. If it's too complex to describe in English, the likelihood is it's simply too complex. Period.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI has no trouble writing AllSpeak code; in our experience it even makes fewer mistakes than when using mainstream languages. This is partly owing to the constrained syntax of AllSpeak and partly to the comprehensive guidance provided to AI when using it.&lt;/p&gt;

&lt;p&gt;Like natural language, AllSpeak relies on an extensive vocabulary instead of complex syntax. When you read a book on an unfamiliar subject or even in a foreign language, the structure of the text seldom causes you any difficulty; it's usually the words themselves. You consult a dictionary for the terms you don't know. When reviewing AllSpeak code, the dictionary is an AI agent: an unfamiliar word or construct in the code is just a question away. "What does this word mean?" or "What is this block doing?" — and the agent explains it in plain language. Recognition, assisted by the dictionary, is enough to review what was written. When complexity is handled by words in the language rather than by syntax, this process becomes far easier. Most of us find complex syntax hard to follow, whereas we can absorb a huge range of vocabulary with little effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why programmers resist
&lt;/h2&gt;

&lt;p&gt;Many programmers find the foregoing argument hard to accept — so used are they to regarding code as something they own. "Reading the code" has always been unpopular, and mostly for good reason. Every cycle, someone promises readable code and fails to deliver — so the scepticism is earned. The difference this time is that the code is no longer written by hand at all. When using AllSpeak, AI is under strict instruction to provide clear and comprehensive documentation for each block of code, for the benefit both of itself and of the humans who will review the code. If this stipulation is enforced, then regardless of what coding language is used, at last we can rely on documentation that's both accurate and complete. But even the best documentation will not save the reader from having to know the language itself — and the expertise that mainstream languages demand is precisely what will be increasingly hard to gain. A language that reads like one you already speak removes that barrier.&lt;/p&gt;

&lt;p&gt;I have recently begun to avoid the word "programmer" when referring to those creating software, because in this scenario the term stops being useful. The people who remain are engineers — and the skills they need are very different from traditional ones. When we delegate to AI the job of creating and managing complex structures, we are free to step back and view the big picture or to zoom in to examine specific details. This is the essence of review, and natural language adds to this by opening the door of opportunity to those previously denied access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review is the coming skill
&lt;/h2&gt;

&lt;p&gt;It is widely accepted that there are fewer and fewer opportunities to gain coding expertise. Review is the coming skill — and if toolchains remain as they are now, few will possess it effectively unless the language of code moves closer to human language.&lt;/p&gt;

&lt;p&gt;The only alternative to accepting — and embracing — more accessible forms of language is to abdicate software generation to machines entirely. That is not a future most of us would choose willingly — and it is the default if we do nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  AllSpeak is a first step, not a destination
&lt;/h2&gt;

&lt;p&gt;AllSpeak is not a final destination; it is a first step towards a new paradigm, one that must gradually become familiar to engineers. The closer the language comes to a human language, the more effective recognition becomes, and the less foreign the code feels.&lt;/p&gt;

&lt;h2&gt;
  
  
  A working example: Doclets
&lt;/h2&gt;

&lt;p&gt;The Doclets project is a working example of the pattern. Neither hand-coded nor vibe-coded, it was produced through exactly the process described above — a succession of deliberately small steps in which AI writes and humans review, block by block. It is an example of a pattern — one of the few patterns that explicitly acknowledges the gap between coding and review and points the way to bridging it.&lt;/p&gt;

&lt;p&gt;Doclets started as a solution to a real-world problem, that of how to organise information, either for an individual or for a small team. Information is typically strewn over a wide range of documentation tools, social media, email, web pages and more.&lt;/p&gt;

&lt;p&gt;The chosen solution was to create a central, searchable repository of Markdown documents ("doclets"), organised into topics, to be read and edited in a browser on any device. A small server process owns the documents and talks to the browser over MQTT.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkexhn6o26h01f5dlx9f1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkexhn6o26h01f5dlx9f1.png" alt="Screenshot: the Doclets reader showing the topic list and query bar" width="790" height="730"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Plain search is a substring match over every doclet's title and content:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvdv6o5i5h5axjhvzvan1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvdv6o5i5h5axjhvzvan1.png" alt="Plain text search" width="800" height="725"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking a result line opens the doclet in prettified (HTML) format, with an option to switch to Markdown for editing.&lt;/p&gt;

&lt;p&gt;A second way to search uses a local language model: tick "LLM query" and ask questions like &lt;em&gt;"list the main topics covered by the doclets in the AllSpeak topic"&lt;/em&gt; — a short prose answer comes back, derived from the collection itself, running entirely on your own machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzegfagxey2r0mxpe1ulj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzegfagxey2r0mxpe1ulj.png" alt="Screenshot: the concise answer in the Doclets reader" width="795" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For small teams, each topic has an owner and a visibility setting: public topics are readable by anyone, private topics only by the owner and named readers, with separate grants for creating, modifying and deleting, and a simple activity log of who did what and when.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it is easy to customise
&lt;/h2&gt;

&lt;p&gt;The construction is deliberately simple — and that is the point. One language for the whole stack (the browser client and the server are both AllSpeak), screens defined declaratively, MQTT for request/reply, and the one heavy component (searching, securing, and the LLM integration) consigned to a Python plugin that adds a small vocabulary of its own to the language.&lt;/p&gt;

&lt;p&gt;Customising Doclets — a new field, a different screen, a renamed command — is often a matter of recognising and adjusting existing code, not writing new code from scratch. That is the review skill this article argues for, applied to a tool you can run today.&lt;/p&gt;

&lt;p&gt;This is a general picture, not a specification — the details live in the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask your agent
&lt;/h2&gt;

&lt;p&gt;Doclets is deliberately and unashamedly an AI project. If you want the detail, point your AI agent at the &lt;a href="https://github.com/easycoder/doclets" rel="noopener noreferrer"&gt;Doclets repository&lt;/a&gt; and ask for a synopsis, a full technical breakdown, or anything in between. Every section of the code carries a doc block explaining &lt;em&gt;why&lt;/em&gt; it exists, so the agent — and you — can read it block by block. Recognition, with the dictionary always at hand.&lt;/p&gt;

&lt;p&gt;The code is not the point; what the code does is the point. If AI writes the code and we review it well, software production becomes a skill of judgment rather than of syntax. That is a change worth embracing.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@cookiethepom?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Cookie the Pom&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/brown-and-white-long-coated-small-dog-wearing-eyeglasses-on-black-laptop-computer-gySMaocSdqs?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>coding</category>
      <category>allspeak</category>
      <category>lowcode</category>
    </item>
    <item>
      <title>How to enjoy programming in a world of AI</title>
      <dc:creator>Graham Trott</dc:creator>
      <pubDate>Sun, 14 Jun 2026 09:38:53 +0000</pubDate>
      <link>https://dev.to/gtanyware/how-to-enjoy-programming-in-a-world-of-ai-5b4e</link>
      <guid>https://dev.to/gtanyware/how-to-enjoy-programming-in-a-world-of-ai-5b4e</guid>
      <description>&lt;p&gt;It's widely accepted that as AI writes more and more of our code, opportunities decrease for newcomers to gain the skills needed to become professional programmers. At the same time, experienced people bemoan the way AI is causing them to gradually lose the skills they took so long and worked so hard to gain.&lt;/p&gt;

&lt;p&gt;Programming, like mathematics and perhaps music, sits in the middle between science and art. As science, it's all about capturing and organising knowledge, and as art, it's the application of creativity to problem-solving. Enjoyment - deep satisfaction or an emotional rush - can be had from either or both of these. But the perception is that AI is depriving us of opportunity, both to learn and to be creative.&lt;/p&gt;

&lt;p&gt;Code written by a human reveals a lot about the coder. Their depth of knowledge of the coding language, their creative use of variable and function names, and even the visual layout of a script. You can often tell at a glance whether the writer truly cared about the code they wrote, or whether it was just a means to an end, to ensure their next pay packet arrives on time.&lt;/p&gt;

&lt;p&gt;As AI improves, it makes fewer mistakes, but it also produces code that is increasingly hard for any human to read and truly understand. Since the job of a human in the coding loop is increasingly not to write but to validate code, this is becoming a problem. So here's my recent story, a series of happy accidents.&lt;/p&gt;

&lt;p&gt;I've been using agentic AI for about a year. First with Copilot in VS Code, then more recently with Claude Code at the command line. At the start, I found Copilot to make a lot of mistakes and to produce rather repetitive, poorly-structured code, but looking back, a lot of that was probably down to my own ignorance of how to control the process.&lt;/p&gt;

&lt;p&gt;The biggest problem I had was in validating JS or Python code. There was just so much of it, often using features of both languages I never fully got to grips with. I've never been a first-class programmer and most of my own code is pure vanilla. The project I was working on was &lt;a href="https://allspeak.ai" rel="noopener noreferrer"&gt;AllSpeak&lt;/a&gt;, based on an English-like scripting language I'd written years earlier to help me create products without having to be a coding expert. AI gave me the chance to bring it up to date. But it was when Copilot started offering me AllScript fragments - uninvited - that I had a complete rethink.&lt;/p&gt;

&lt;p&gt;The assumption usually made is that code must be written in a modern, high-level language like JS or Python. This is quite valid when a human is doing the coding, but it falls down badly for AI. The problem is, these languages are too complex, and they suffer from inconsistencies AI finds difficult to spot. Documentation is often out of date, and new versions of the language can break older code. Additionally, many features of these language are there as guardrails to stop humans making coding errors that gobble up memory or lie in wait to misbehave in the future. Since AI doesn't make the same mistakes as humans it doesn't need these guardrails; in fact it makes AI's job harder, not easier.&lt;/p&gt;

&lt;p&gt;An obvious path revealed itself. If the agent was trying to write code in AllSpeak, why not let it finish the job? I built an &lt;code&gt;AGENT.md&lt;/code&gt; file that instructed the agent to code in my language wherever possible, and gave links to the repo and to documentation that would help it do this without making errors.&lt;/p&gt;

&lt;p&gt;The benefit was immediate. Instead of having to wade through huge piles of JS and Python, I could now see the application being built in something much closer to English. But it went much further than this. Every now and then, the agent would guess at a construct that wasn't valid, using its knowledge of other coding languages. This was usually down to limitations in the available documentation, but I figured that if the agent wanted to use such a construct, as long as it was syntactically valid and consistent with the rest of the language, it should probably be added. So I set up a permanent second session that deals with additions and changes to AllSpeak, and a rule that if the agent can suggest an improvement, it should do so. &lt;/p&gt;

&lt;p&gt;Because AllSpeak is growing in this way, it might be regarded as an impediment to coding, given that it was designed in the first place to make coding simpler. But this changes when AI is doing the coding. The human's job becomes not to write code but to read it, and because the language is close to English, the meanings of new syntax can be made fairly obvious. I can pile in a load of new syntax that reduces repetitive boilerplate to a few words. I no longer have to &lt;em&gt;remember&lt;/em&gt; all the syntax, merely to &lt;em&gt;recognise&lt;/em&gt; it. This is a big difference.&lt;/p&gt;

&lt;p&gt;The final part of this story so far (there may be more to come) was that I'm just an individual with a limited budget. Claude Code does a superb job, but it can get expensive. The pricing is non-linear; spending more gives you exponentially more tokens, but I was ending up not using my allowance. Reducing the cost meant I overran and had to top up.&lt;/p&gt;

&lt;p&gt;Then I saw a mention that DeepSeek costs about 50 times less (yes, that's 2%) than Claude Code, so I had to try it. And it's true; after a couple of weeks of daily but not intensive work it's cost me $0.46. However, it's not as good at coding as Claude. It's quick, but it makes more errors, and from the verbose output it emits while working, it seems to struggle from time to time. How much of this is down to my still less-than-perfect documentation is hard to tell, but I definitely have to intervene more often. I understand that deepseek-v4-flash is optimised more for managing huge datasets than it is for coding, which could well be the explanation.&lt;/p&gt;

&lt;p&gt;But this limitation brings an unexpected benefit. When working with Claude Code, I often felt myself to be merely a bit player. I gave the requests, but then had little part to play in how things were done as mistakes were few and far between. With DeepSeek I feel more like an equal. We have different roles; DS does the coding and I review it. And because it's written in something close to English, I can follow it far more easily than I would have, had it been JS or Python. I can spot things that aren't right and suggest improvements. I've started to feel like a programmer again, that I really have a role to play in the coding process. And that's where the enjoyment has returned.&lt;/p&gt;

&lt;p&gt;So what started as a series of serendipitous accidents followed by a cost-saving trial has turned out to be a partnership of equals, restoring my enjoyment of the coding process. One conclusion I draw from this is that maybe instead of always assuming the latest model is better, consider whether it leaves you with a real part to play in that process, or whether you'll get more enjoyment out of taking on the role of teacher and helping a lesser model do its job, while saving money at the same time. Don't forget, happy people produce better results.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>discuss</category>
      <category>learning</category>
      <category>programming</category>
    </item>
    <item>
      <title>When AI writes the code, what should humans actually read?</title>
      <dc:creator>Graham Trott</dc:creator>
      <pubDate>Mon, 11 May 2026 10:04:16 +0000</pubDate>
      <link>https://dev.to/gtanyware/when-ai-writes-the-code-what-should-humans-actually-read-4k1d</link>
      <guid>https://dev.to/gtanyware/when-ai-writes-the-code-what-should-humans-actually-read-4k1d</guid>
      <description>&lt;p&gt;There is an open secret in the world of vibe coding. The people commissioning the work — the ones with the product idea, the domain expertise, the actual customer in mind — usually cannot read the output. They prompt, the model produces, and the result is a tower of TypeScript or Python they accept on faith because they have no way to verify it. The validation step gets quietly skipped. "It runs" becomes "it's correct."&lt;/p&gt;

&lt;p&gt;This is not a moral failing. It's a tooling problem. And I think the way out of it is hiding in plain sight.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with normal code in an AI-first workflow
&lt;/h2&gt;

&lt;p&gt;If your premise is that AI is going to do most of the routine writing of code, then the human's job shifts. We move from authors to reviewers. From "did I express this correctly?" to "did the machine express what I meant?"&lt;/p&gt;

&lt;p&gt;Reviewing is a different job from writing, and it has different tooling needs. When you're writing, you want a fast feedback loop — autocomplete, jump-to-definition, a fast test runner. When you're reviewing, you want comprehension support — context next to the code, an explanation of &lt;em&gt;why&lt;/em&gt; this section exists, and confidence that what you're reading is actually what's running.&lt;/p&gt;

&lt;p&gt;Most editors are still optimised for the writer. The reviewer has to piece things together: read the code, hunt for a docstring above it, hope the docstring still matches, then mentally verify against intent. For an experienced developer writing their own code, this can be fast. For a vibe coder reviewing AI output, it's almost impossible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two things to fix
&lt;/h2&gt;

&lt;p&gt;I've been working on the editor side of this for &lt;a href="https://allspeak.ai" rel="noopener noreferrer"&gt;AllSpeak&lt;/a&gt;, a multilingual scripting language. AllSpeak allows the same programs to be written in French, German, Italian, or any other language we add. The combination of natural-language source with a review-first editor is starting to look like a real answer to the validation gap.&lt;/p&gt;

&lt;p&gt;The first screenshot below shows the editor in normal ("raw") mode, showing a documentation block followed by some code. Because of the color-coding, the eye skips over the documentation quite easily; it's not meant to be read here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffd6t1qotbcotavsazxve.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffd6t1qotbcotavsazxve.png" alt=" " width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second screenshot shows the editor in Blocks mode, displaying the same piece of code but with its documentation in the right-hand pane, making code review far simpler. On the left is a list of all the blocks, for navigation, or you can use the up and down arrows in the toolbar. This is just a starting point; the editor could have a long way to go.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5d3do9kx9idsxakm9jvd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5d3do9kx9idsxakm9jvd.png" alt=" " width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maintaining such a structure would be a daunting task without the help of AI. This is an almost free gift we should take full advantage of.&lt;/p&gt;

&lt;p&gt;There are two specific changes I'm making.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, sections of code get a documentation block above them&lt;/strong&gt;, in a structured comment format. Nothing radical there — literate programming has done variations on this for decades. The new bit is that each block contains two SHA hashes: one for the documentation, one for the code section it describes. If either changes without the other being deliberately re-paired, the editor flags drift.&lt;/p&gt;

&lt;p&gt;This is cheap, mechanical, and solves a problem that has plagued every codebase I've ever worked on. Documentation rots silently. Cryptographic pairing makes the rot clearly audible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second, the editor gains a side-by-side mode&lt;/strong&gt; that shows one section at a time, code on one side, its documentation on the other. The reviewer sees a small, focused unit and can ask the only question that matters: does the code do what the prose says it does?&lt;/p&gt;

&lt;p&gt;That's a comparison task, not a comprehension task. Comparison is much easier than comprehension for non-experts — and that's the entire point.&lt;/p&gt;

&lt;p&gt;Of course, all of this only becomes possible when AI is doing the coding, as is increasingly the case. Human coders, however professional, don't like to maintain comprehensive documentation for their code. Documentation gets in the way of coding and is usually regarded as an imposition, so the bare minimum is all that gets written. An agent, on the other hand, has endless patience and is more than willing to take on such a task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters more for AllSpeak than for Python
&lt;/h2&gt;

&lt;p&gt;Here's where the language choice does real work. If the code is dense Python with framework conventions a non-developer can't parse, asking "does the code match the prose?" is still a comprehension task in disguise. The reviewer has to understand the code first, then compare. The validation gap stays roughly where it was.&lt;/p&gt;

&lt;p&gt;If the code is AllSpeak — close enough to English that a careful reader can follow it line by line — the gap narrows considerably. The reviewer reads two pieces of natural-ish text and checks whether they agree. They don't need to know what a decorator is, or how async resolves, or which way the data flows through a hook. They just need to read.&lt;/p&gt;

&lt;p&gt;That's the leverage point. AllSpeak by itself simplifies syntax; the review tooling by itself simplifies workflow; together they change who can credibly validate generated code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Files become packages, not text
&lt;/h2&gt;

&lt;p&gt;A side effect of all this: a source file is no longer just a sequence of statements. It's a structured package containing code sections, documentation sections, and the cryptographic links between them. The raw form might look a bit ugly opened in &lt;code&gt;vim&lt;/code&gt; — comment blocks dominate — but it's not really meant to be read raw any more than a minified JavaScript bundle is.&lt;/p&gt;

&lt;p&gt;I want to be careful with this claim, though. There's a temptation to push it further than it deserves. "Humans don't need to read raw code any more" is not quite right. Sometimes the editor is unavailable. Sometimes you're debugging at 2am with &lt;code&gt;grep&lt;/code&gt; and a terminal. Sometimes a future tool needs to interoperate with your files and the only sane interface is plain text. The defensible version of the claim is softer: humans should rarely &lt;em&gt;need&lt;/em&gt; to read the raw form, but the format should remain legible in extremis. AllSpeak's plain-English nature preserves that floor even with the scaffolding around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd encourage other tool-makers to think about
&lt;/h2&gt;

&lt;p&gt;If the future of coding is mostly machine-written, the tooling we should be investing in is the tooling that helps humans &lt;em&gt;check&lt;/em&gt; what the machines produced. That's underbuilt right now. The current generation of AI coding tools — the Lovables and v0s and Bolts of the world — focus almost entirely on generation. They produce React, Next.js, the standard opaque stack, and they assume the user will accept whatever comes out. For users who can't read the output, that assumption is shaky at best.&lt;/p&gt;

&lt;p&gt;A few things I think are worth borrowing or stealing from what I'm building:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Treat documentation as a first-class artefact paired with code, not a comment that floats nearby&lt;/li&gt;
&lt;li&gt;Use cryptographic pairing or some equivalent to make drift visible&lt;/li&gt;
&lt;li&gt;Build review modes that show one unit at a time with context attached&lt;/li&gt;
&lt;li&gt;Pick a source language whose readability matches the average reviewer's skill level&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last one is the hardest sell to the developer audience because it sounds like a step backwards. But if you accept the premise that AI is going to write most of the code and humans are going to review most of it, then optimising the source language for &lt;em&gt;human reading&lt;/em&gt; — even at some cost to expressive density — starts to look like exactly the right trade.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small invitation
&lt;/h2&gt;

&lt;p&gt;I'm writing this as the AllSpeak editor work progresses. If you're building tools in this space, or if you're a vibe coder who's quietly worried about whether you can really vouch for what you're shipping, I'd be very interested in hearing from you.&lt;/p&gt;

&lt;p&gt;The future where AI writes everything and humans rubber-stamp it is the bad version. The future where AI writes everything and humans actually read and approve it is the one worth building toward. The difference is mostly about tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Postscript
&lt;/h2&gt;

&lt;p&gt;The editor described here is written in the JS implementation of AllSpeak to run in a browser. It is served from &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt; by a smaller AllSpeak module written in the Python implementation, which has access to all files and system resources.&lt;/p&gt;

&lt;p&gt;At the time of writing, the editor (asedit.as) comprises 944 lines of AllSpeak code, 173 lines of comment and 44 blank lines. The block view addition was added in one day by Claude Code, using continuous prompt/review.&lt;/p&gt;

&lt;p&gt;This document was proposed and argued by me, written by Claude and edited by me. I take full responsibility for the content.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@vladimir_d?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Volodymyr Dobrovolskyy&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/a-cat-sitting-in-front-of-a-computer-monitor-KrYbarbAx5s?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>code</category>
      <category>ai</category>
      <category>review</category>
    </item>
    <item>
      <title>Claude built me a high-level coding system in 4 European languages</title>
      <dc:creator>Graham Trott</dc:creator>
      <pubDate>Wed, 29 Apr 2026 16:29:51 +0000</pubDate>
      <link>https://dev.to/gtanyware/claude-built-me-a-high-level-coding-system-in-4-european-languages-461a</link>
      <guid>https://dev.to/gtanyware/claude-built-me-a-high-level-coding-system-in-4-european-languages-461a</guid>
      <description>&lt;p&gt;Years ago I wrote myself a high-level scripting language, mainly because I'm not a great programmer and I wanted something easier to handle all the jobs waiting to be done. It was pretty successful, in that the jobs got completed and they're easy to dive into years later to fix problems.&lt;/p&gt;

&lt;p&gt;When I discovered agentic AI about a year ago I aimed it at one of those projects, to help me write a language extension needed for a new feature. To my surprise, after a while the AI started offering me code in my own scripting language, not just in the Python it was built with. The code was pretty awful but it gradually got better.&lt;/p&gt;

&lt;p&gt;I moved the project over to Claude Code a couple of months ago, and with careful prompting Claude was soon able to do virtually all my coding for me in script rather than Python or JS. This made code verification simpler because English-like scripts are much easier to read than the traditional high-level language code needed to carry the same meaning.&lt;/p&gt;

&lt;p&gt;Occasionally we come across something the script doesn't handle well, so we go back to Python/JS to extend or revise the compiler and runtime. For more complex, self-contained chunks of functionality we build plugins, also in Python or JS, that seamlessly extend the language and can be imported when needed rather than having to be present all the time. We drew in various bits and pieces from the original project, such as a reference manual, a tutorial and a programmers' playground, and built a website to describe what we were doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonjour / Guten Tag / Buongiorno
&lt;/h2&gt;

&lt;p&gt;At some point I had the crazy idea of internationalizing the scripting language. All mainstream programming languages are based on English words, which gives native English speakers a distinct - and to my mind somewhat unfair - advantage. I consulted Claude, who explained that it wasn't necessary to rewrite the whole thing, just put a language pack - mostly just a large JSON script - in front of the compiler, to deal with different vocabulary, syntax, word order and so on. The 'intents' remain the same; addition is still addition and a subroutine call is still a subroutine call. The system comprises a single compiler and runtime, plus a set of language packs.&lt;/p&gt;

&lt;p&gt;I picked Italian, the language I know best other than English, and told Claude to get on with it. This went way better than expected, so I added French and German and named the project AllSpeak*. For all three languages, the translations are a bit clunky in places and could do with being proofread by native speakers (any volunteers?), but there's little doubt the meaning is clear. At worst they come over a bit like Yoda, with mangled syntax and odd word order.&lt;/p&gt;

&lt;p&gt;Take a very simple example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  variable Greeting
  put `Hello, AllSpeak!` into Greeting
  log Greeting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in French is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  language fr

  variable Salutation
  mets `Bonjour, AllSpeak !` dans Salutation
  journalise Salutation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in German is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  language de

  variable Gruss
  lege `Hallo, AllSpeak!` in Gruss
  logge Gruss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in Italian is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  language it

  variabile Saluto
  metti `Ciao, AllSpeak!` in Saluto
  registra Saluto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what use is a set of multilingual scripting languages? Well, with the right primer packs to describe the vocabulary and syntax, Claude Code and other agents can use them in place of mainstream computer languages to handle a wide range of software applications, including where those doing the prompting are not native English speakers. With AI doing all the coding, the choice of programming language is not important; the main thing is readability by those charged with validating the code. Unlike mainstream programming languages like Python or JS, the code produced by AllSpeak can be read and understood not just by professional coders but also by some of the people who will be using the finished software. And with the help of the language packs, the AI agent generates code in the same language as the prompts it was given; code that compiles and runs exactly the same way as the English version because it uses the same compiler and runtime.&lt;/p&gt;

&lt;p&gt;But in the end, I don't know. This was mostly an intellectual exercise; an exploration of what's possible. I have a feeling it's an indication of one direction that coding could be heading, but only time will tell. &lt;/p&gt;

&lt;p&gt;Anyone curious enough to try it out is welcome to visit the  &lt;a href="https://allspeak.ai" rel="noopener noreferrer"&gt;AllSpeak&lt;/a&gt; website.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@dbeamer_jpg?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Drew Beamer&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/pink-and-yellow-hello-neon-light-3SIXZisims4?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;"Allspeak is the divine language of the Norse gods - anything that can communicate can understand it, and be understood in turn."&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>claude</category>
      <category>programming</category>
    </item>
    <item>
      <title>AI has reprogrammed me</title>
      <dc:creator>Graham Trott</dc:creator>
      <pubDate>Fri, 24 Apr 2026 07:52:22 +0000</pubDate>
      <link>https://dev.to/gtanyware/ai-has-reprogrammed-me-3g2b</link>
      <guid>https://dev.to/gtanyware/ai-has-reprogrammed-me-3g2b</guid>
      <description>&lt;p&gt;It's quite scary how reluctant I feel to dive into JS or Python coding since I started watching Claude Code do it. I'm happy enough to guide the process and examine changes made to the code, but actually spending hours writing the stuff - &lt;em&gt;meh&lt;/em&gt;. All this in a couple of short months. Years of practice are as nothing. I've been reprogrammed. &lt;/p&gt;

&lt;p&gt;I wonder how many others feel the same way once they too have been utterly outclassed by AI. Some still carry on wanting to do it but I suspect they're in a minority that's shrinking daily.&lt;/p&gt;

&lt;p&gt;It's a bit like crafts such as woodworking or embroidery. Some do them as a hobby, for fun or for relaxation, but as soon as productivity becomes an issue there's no way you're ever going to compete. Better to develop the ability to guide the process, spot errors and help fix them. Your experience is not lost - it transforms into new skills. &lt;/p&gt;

&lt;p&gt;For example, one of the new must-have skills is the ability to speed-read. There's no way you can follow Claude's thinking without it, as it's almost impossible to study each sentence as the screen scrolls past. You need to use pattern-recognition that works on large blocks of moving text. Some are better than others at this but we're all able to do it - we just need to practice. &lt;/p&gt;

&lt;p&gt;The fact is, few of us are the coding gods we long believed ourselves to be. Programming is something that has always made us stand out from the crowd. If we're going to maintain that distinction we have to adapt. Fast.&lt;/p&gt;

&lt;p&gt;A good friend of mine is a proofreader by profession. He can spot swapped letters or a misplaced apostrophe in a full page of text at a glance. Musicians can hear a duff note from a single instrument in an orchestra. Likewise, most good programmers can home in on poor code instinctively and without even thinking about it. Our coding skills need not be wasted but they do need to adapt to a rapidly changing world.&lt;/p&gt;

&lt;p&gt;One thing we must do is resist the temptation to take AI engines on trust. Be picky; make them work hard to please you, whether it's style factors like your preference for open text with lots of whitespace, or choice of variable names, or replacing deep indenting with functions... make them do it. Keep nagging them till it's just right for you. That way at least you'll preserve your self-respect. For you &lt;strong&gt;&lt;em&gt;are&lt;/em&gt;&lt;/strong&gt; superior; you may not be able to churn out code at factory speed but you sure as hell can tell when it ain't right.&lt;/p&gt;

&lt;p&gt;AI is here to stay. Don't be intimidated and don't pretend you can carry on without it. Embrace it and prove that things go better when you are at the helm. Accept the inevitable reprogramming of yourself and take control of it. &lt;/p&gt;

</description>
      <category>ai</category>
      <category>coding</category>
      <category>software</category>
    </item>
    <item>
      <title>AI Doesn't Need Your Programming Language</title>
      <dc:creator>Graham Trott</dc:creator>
      <pubDate>Thu, 09 Apr 2026 15:05:57 +0000</pubDate>
      <link>https://dev.to/gtanyware/ai-doesnt-need-your-programming-language-2bg0</link>
      <guid>https://dev.to/gtanyware/ai-doesnt-need-your-programming-language-2bg0</guid>
      <description>&lt;p&gt;&lt;em&gt;Why the future of code is simpler than you think — and why that matters for everyone.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Question Nobody's Asking
&lt;/h2&gt;

&lt;p&gt;Here's something odd about the current AI coding revolution: we're using AI to generate JavaScript, Python, TypeScript — languages that were designed for &lt;em&gt;humans&lt;/em&gt; to write. But humans aren't writing the code any more. The AI is.&lt;/p&gt;

&lt;p&gt;So why are we still optimising for human writability?&lt;/p&gt;

&lt;p&gt;The answer, of course, is that humans still need to &lt;em&gt;read&lt;/em&gt; the code — to verify it, debug it, and maintain it. But here's the thing: the languages we use make that harder than it needs to be. A hundred lines of React with hooks, closures, and async/await is not easy for a human to verify. It wasn't easy when a human wrote it. It's certainly not easy when an AI wrote it and you're trying to decide whether to trust it.&lt;/p&gt;

&lt;p&gt;What if the AI wrote in something simpler?&lt;/p&gt;




&lt;h2&gt;
  
  
  The Case for Simpler Languages
&lt;/h2&gt;

&lt;p&gt;This isn't about dumbing things down. It's about recognising that the economics of code have changed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI makes fewer mistakes in constrained grammars.&lt;/strong&gt; Give an LLM a language with a small, consistent vocabulary and no syntactic ambiguity, and its error rate drops dramatically compared to JavaScript or Python. Fewer mistakes means less debugging, less rework, less cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI uses fewer resources with simpler output.&lt;/strong&gt; Generating 30 lines of readable script costs less in tokens than generating 200 lines of framework code that does the same thing. At scale, this is a real cost difference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Humans can actually verify simple code.&lt;/strong&gt; This is the critical point. The value of AI-generated code is zero if you can't tell whether it's correct. A non-specialist can look at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight applescript"&gt;&lt;code&gt;&lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="nv"&gt;click&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;SaveButton&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;begin&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nb"&gt;put&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;NameField&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;rest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;save&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="nv"&gt;Saved&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and know exactly what it does. Try that with the equivalent React component.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simple languages can be made fast.&lt;/strong&gt; A high-level scripting language written in WebAssembly or C would be indistinguishable in performance from today's mainstream languages for most applications. The simplicity is in the surface syntax, not the execution model.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where This Is Heading
&lt;/h2&gt;

&lt;p&gt;Let me make a prediction that might be uncomfortable:&lt;/p&gt;

&lt;p&gt;Before long, most code will be written by AI and validated — if at all — by humans. The role of the human shifts from &lt;em&gt;author&lt;/em&gt; to &lt;em&gt;reviewer&lt;/em&gt;. And reviewers need readable code far more than authors do.&lt;/p&gt;

&lt;p&gt;When that happens, the value proposition of complex languages changes. JavaScript's flexibility, Python's ecosystem depth, TypeScript's type system — these are advantages for human authors. For AI authors with human reviewers, they're liabilities. They increase the surface area for errors and make verification harder.&lt;/p&gt;

&lt;p&gt;What you want instead is a &lt;em&gt;lingua franca&lt;/em&gt; — something simple enough that AI rarely gets it wrong, readable enough that humans can verify it at a glance, and powerful enough to build real applications. The complex languages don't disappear, but they move to the edges: systems programming, performance-critical code, specialised domains. The broad middle — the apps, the tools, the dashboards, the automations — gets written in something more like natural language.&lt;/p&gt;

&lt;p&gt;This isn't science fiction. It's already starting.&lt;/p&gt;




&lt;h2&gt;
  
  
  AllSpeak: A Working Example
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://allspeak.ai" rel="noopener noreferrer"&gt;AllSpeak&lt;/a&gt; is a scripting language built around this idea. Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myButton&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;output&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backgroundColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pink&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;button MyButton
div Output
attach MyButton to `mybutton`
attach Output to `output`
...
on click MyButton
begin
    set style `background` of Output to `pink`
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AllSpeak runs in two environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In the browser&lt;/strong&gt; — load a single JavaScript file, write scripts in your HTML&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;As a command-line tool&lt;/strong&gt; — &lt;code&gt;pip install allspeak-ai&lt;/code&gt;, run &lt;code&gt;.as&lt;/code&gt; files directly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No build step. No package manager. No framework. One file in, working application out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why AI loves it
&lt;/h3&gt;

&lt;p&gt;Claude Code generates correct AllSpeak on the first attempt almost every time. The grammar is constrained and consistent — no semicolons, no curly braces, no type declarations, no ambiguous syntax. When it does make a mistake, the error is obvious and easy to fix.&lt;/p&gt;

&lt;p&gt;And because AllSpeak scripts are short and readable, Claude Code can re-read its own output and make targeted changes. This is fundamentally different from asking an AI to modify 500 lines of opaque JavaScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Working with AI changes how you think
&lt;/h3&gt;

&lt;p&gt;There's something deeper here too. Traditional development is bottom-up: define your data structures, write your utilities, build your components, assemble them into a system. You start with what you can hold in your head.&lt;/p&gt;

&lt;p&gt;AI-assisted development inverts this. You start at the top — describe the whole application, sketch out the flow, name the pieces — and drill down into the details. The AI generates a working skeleton from your high-level description, and you refine from there. With AllSpeak, this feels natural: you're describing what you want in something close to plain language, and the AI fills in the structure underneath.&lt;/p&gt;

&lt;p&gt;This isn't a minor productivity gain. It changes &lt;em&gt;how you think about the problem&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  And Then It Gets Interesting: Any Language
&lt;/h2&gt;

&lt;p&gt;Once you have a simple, readable scripting language, something unexpected becomes possible. If the surface syntax is just a mapping from human words to internal opcodes, why does it have to be English?&lt;/p&gt;

&lt;p&gt;AllSpeak uses &lt;strong&gt;language packs&lt;/strong&gt; — JSON files that map keywords, grammar patterns, and connector words to a shared set of internal commands. The English pack maps &lt;code&gt;put&lt;/code&gt; to the PUT opcode. The Italian pack maps &lt;code&gt;metti&lt;/code&gt;. Both compile to identical bytecode.&lt;/p&gt;

&lt;p&gt;Last week, with Claude Code doing the heavy lifting and me providing Italian knowledge, I built a fully working Italian variant — complete with documentation — in a single day.&lt;/p&gt;

&lt;p&gt;Here's what the same program looks like in both languages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;English:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variable Message
put `Hello from the world!` into Message
log Message
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Italian:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;language it

variabile Messaggio
metti `Ciao dal mondo!` in Messaggio
registra Messaggio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same engine. Same bytecode. An Italian speaker reads the second version and understands it immediately — no English required.&lt;/p&gt;

&lt;p&gt;There are roughly 1.5 billion English speakers in the world. There are roughly 6.5 billion who aren't. Every mainstream programming language uses English keywords. AllSpeak removes that barrier.&lt;/p&gt;

&lt;p&gt;But here's what matters for English-speaking developers too: the architecture that makes multilingual programming possible is the &lt;em&gt;same&lt;/em&gt; architecture that makes AI-friendly programming possible. The constraint, the simplicity, the clean mapping from intent to behaviour — those properties serve both goals simultaneously.&lt;/p&gt;




&lt;h2&gt;
  
  
  Adding a New Language
&lt;/h2&gt;

&lt;p&gt;This is the part that surprised me.&lt;/p&gt;

&lt;p&gt;Creating the Italian variant required:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A language pack JSON file&lt;/strong&gt; — mapping ~150 opcodes and ~80 connector words&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compiler adjustments&lt;/strong&gt; — making the parser read the active language pack instead of hardcoded English&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime adjustments&lt;/strong&gt; — localised error messages and status output&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt; — translating the primer and editor UI&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The process for adding further languages is now well-understood:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A native speaker with basic programming familiarity&lt;/strong&gt; can create a new variant in a few days&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No deep expertise required&lt;/strong&gt; — understand variables, loops, and conditions, not the engine internals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The engine doesn't change&lt;/strong&gt; — only the language pack and docs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What AllSpeak Is Best Suited For
&lt;/h2&gt;

&lt;p&gt;AllSpeak is not trying to replace Python or JavaScript. It occupies the broad middle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Internal tools and dashboards&lt;/strong&gt; — forms, data viewers, admin panels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personal productivity apps&lt;/strong&gt; — task lists, habit trackers, timers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prototyping&lt;/strong&gt; — working demos before committing to a full stack&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Education&lt;/strong&gt; — where tooling complexity distracts from learning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation scripts&lt;/strong&gt; — file processing, report generation, data transformation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IoT and kiosk applications&lt;/strong&gt; — single-purpose apps with predictable state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For deep OS integration or complex UI component trees, use a mainstream language. AllSpeak handles the human-level logic — flow, decisions, user interaction — while plugins handle the heavy lifting in native code.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Workflow
&lt;/h2&gt;

&lt;p&gt;The recommended setup uses three tools:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code&lt;/strong&gt; — Anthropic's agentic CLI, for writing and modifying AllSpeak scripts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AllSpeak Editor&lt;/strong&gt; — browser-based editor with syntax highlighting, auto-save, and file browsing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AllSpeak runtime&lt;/strong&gt; — &lt;code&gt;pip install allspeak-ai&lt;/code&gt; for CLI, or a single &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag for browser apps&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Getting started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;myproject &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;myproject
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt; allspeak-ai
&lt;span class="c"&gt;# Download the starter pack for your language from allspeak.ai&lt;/span&gt;
unzip allspeak-xx.zip &lt;span class="c"&gt;# where xx is your language code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then launch Claude Code and say: "I want to build a weather dashboard."&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;AllSpeak is actively looking for contributors — particularly native speakers who want to bring programming to their language community. But also developers who are interested in the broader question: what does programming look like when AI writes most of the code and humans are primarily reviewers?&lt;/p&gt;

&lt;p&gt;The answer might be simpler than any of us expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get started:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://allspeak.ai" rel="noopener noreferrer"&gt;AllSpeak website&lt;/a&gt; — overview, primer, and AI manual&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://claude.ai/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; — the AI assistant that makes it practical&lt;/li&gt;
&lt;li&gt;Email: &lt;a href="mailto:info@allspeak.ai"&gt;info@allspeak.ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;AllSpeak is named after the Asgardian ability to communicate in any language. It started as an English-only scripting language called EasyCoder. The multilingual architecture was added in April 2026 with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>coding</category>
      <category>programming</category>
      <category>ai</category>
      <category>languages</category>
    </item>
    <item>
      <title>The Catch-22 of programming</title>
      <dc:creator>Graham Trott</dc:creator>
      <pubDate>Sat, 29 Nov 2025 21:06:33 +0000</pubDate>
      <link>https://dev.to/gtanyware/the-catch-22-of-programming-2e1p</link>
      <guid>https://dev.to/gtanyware/the-catch-22-of-programming-2e1p</guid>
      <description>&lt;p&gt;The Catch-22:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Only skilled programmers can guide AI to create good software.&lt;/li&gt;
&lt;li&gt;It takes a long time to gain the skills to become a skilled programmer, starting with a long period of simple coding at the entry level.&lt;/li&gt;
&lt;li&gt;AI is taking away the jobs of entry-level programmers.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Becoming a programmer
&lt;/h3&gt;

&lt;p&gt;In the early days of computing, the route to becoming a programmer was straightforward. You just did it. Training courses were initially nonexistent, so we had to learn from textbooks and data sheets. The ones who had the motivation to stick with it were the ones who became professional programmers.&lt;/p&gt;

&lt;p&gt;After that, in some ways it got a lot easier. Information became more plentiful, and with the arrival of first email then the World Wide Web, there were people you could ask and articles you could read without having to order a book and wait for it to arrive. Courses became available for pretty much anything.&lt;/p&gt;

&lt;p&gt;In other ways, though, it didn’t get easier. Software became more complex, and a myriad of tools and libraries became regarded as essential things to know. Job listings started to fill up with acronyms and left many applicants feeling their efforts had all been for nothing, as the prize continued to remain just out of reach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Programming with an LLM
&lt;/h3&gt;

&lt;p&gt;Then along came AI, and suddenly it seemed a programmer's life became simpler again. No more need to remember every function in a library; just ask AI to find the one you need. But just as we started to get used to this new way of working, these upstart AI systems started to go for our jobs. And now we’re told that pretty much all programming will soon be done entirely by AI engines.&lt;/p&gt;

&lt;p&gt;Or so it seems. However, if you actually ask AI to write software, you’ll soon discover that what comes back might be classifiable as a prototype but is likely to be totally unfit for delivery as a finished product. The only way to ensure a good product is to create a bullet-proof prompt to describe what you want, then closely monitor the entire process from start to finish, enforce good working practices and test everything as it gets written. The only person who can do that is a competent programmer.&lt;/p&gt;

&lt;p&gt;So we have a Catch-22 situation. To get a good product you need to be an expert to drive AI in the right way. But because AI is taking over all the mundane programming work, few will ever spend the time and effort to become expert. Who in their right mind spends hours churning out boilerplate code when AI will spit it out in seconds? But if you don’t do it yourself you’ll never really learn how to, and you won’t be able to spot the mistakes made by AI. Likewise, you’ll never really know what any given programming language is capable of, and whether AI is making best use of it.&lt;/p&gt;

&lt;p&gt;Or to put it more concisely: To get a job you need skills, but to get skills you need a job.&lt;/p&gt;

&lt;h3&gt;
  
  
  The crux of the matter
&lt;/h3&gt;

&lt;p&gt;This is a situation that is only just developing but is set to get pretty serious quite quickly as the supply of top-level programmers dries up. The risk is that AI-generated products will frequently never develop past the prototype stage, while many will remain incomplete and generally unfit for purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are our options?
&lt;/h2&gt;

&lt;p&gt;At the heart of the problem is the way we communicate with computers. There are basically 2 ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Using traditional high-level languages. This means code written in C++, Java, Python or any other mainstream programming language. As explained above, fewer people will become skilled at this work as AI takes over the lower levels of coding.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using natural language, e.g. English. This is the new way that has come into its own with the arrival of AI. However, program code must be unambiguous and human language is usually anything but. This is the enemy of AI, as it will usually work with what it thinks you mean rather than question a possible ambiguity, so the result may be subtly or even grossly different to that which you wanted.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Software can be categorised in several ways, but one that I find useful is to distinguish between tools and products. Tools include operating systems and code libraries, and products are the things that are built with the tools. The thing that for me stands out like a lighthouse beacon is that coders use the same language for both, when in fact they often require very different forms of language. A computer algorithm expressed in English is clumsy and hard to follow, and a set of business rules that makes perfect sense in English becomes impenetrable when converted to Python or Java. Real-world requirements are often long chains of ‘if-then-else’ logic, with a frequent need for ‘goto’ to jump from one place to another, which is of course a complete no-no in the world of structured programming. Maybe what we need is a different form of language.&lt;/p&gt;

&lt;p&gt;However much automation is provided, there will always be skilled toolmakers. Although it has limited storage capacity, the human brain is capable of insights no machine can match. So why waste such talent by asking such people to simply mind AI while it does all the hard work? Right now, the answer is that there is no separate class of coder with the ability to express clearly what is needed and guide the AI to create it, without having taken the long route to becoming a professional user of conventional languages and tools.&lt;/p&gt;

&lt;p&gt;Some people are better than others at expressing clear logic. Many become lawyers, and they use a highly constrained form of English that most of us find difficult to understand, but which has that vital property of being unambiguous. Maybe we need something akin to this when we want to guide AI?&lt;/p&gt;

&lt;h2&gt;
  
  
  An example
&lt;/h2&gt;

&lt;p&gt;To help clarify where I'm heading, here's an example. Let's suppose the task is to create a new app with a windowing user interface. The first thing is to decide where it will run, so I'll say on any desktop OS. This probably suggests Python should be the language to code for, and I'll constrain it a bit further by specifying PySide to handle the graphics.&lt;/p&gt;

&lt;p&gt;The app will mimic a traditional web page, with a central data area, left and right side bars, a title bar and a footer. There's a menu bar with a single menu having 2 items, About and Exit, each with simple behaviours. It looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxl0crefwy0hey4z29qy5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxl0crefwy0hey4z29qy5.png" alt="An example" width="638" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhnag4c93lcqg8lpriwmn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhnag4c93lcqg8lpriwmn.png" alt="About menu" width="641" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's an AI prompt to create the app as shown above:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Using PySide6, create a main window with the following attributes:&lt;br&gt;
Location: 100,100&lt;br&gt;
Size: 640,480&lt;br&gt;
Title: My New App&lt;br&gt;
The window contains 3 rows.&lt;br&gt;
The first row is a panel taking 20% of the window height, containing a label with the text ‘Header’.&lt;br&gt;
The second row has a height that is what is left after the other 2 rows are created. It has 3 elements: 1 - a panel taking 20% of the window width, with a label having the text ‘Left’. 2 - a panel taking whatever width is left after the other 2 items are created, with a label having the text ‘Center’. 3 - a panel taking 15% of the window width with a label having the text ‘Right’.&lt;br&gt;
The third row is a panel taking 15% of the window height, containing a label with the text ‘Footer’.&lt;br&gt;
The window has a menu bar with a single ‘File’menu having 2 items:&lt;br&gt;
An ‘About’ item that when clicked shows a simple dialog.&lt;br&gt;
An ‘Exit’ item that terminates the app when clicked.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a fairly clumsy way to program. It's largely unstructured, and when changes are needed whole sections may have to be rewritten. Most importantly, to avoid ambiguity it requires discipline in the choice of wording; the kind of discipline that only comes with experience. Since there’s no “compiler” to parse the text and spot problems with language or syntax, errors can easily slip through unnoticed.&lt;/p&gt;

&lt;p&gt;So let’s see if we can use a simplified form of English to describe this window. The vocabulary I’ve chosen is arbitrary and many syntax variations can be accommodated to provde alternative ways of expressing the same thing, as in English. To be clear, when I refer to this as English I mean it's text that is mostly if not completely confined to English words, not mathematical symbols. And it’s terse; this is a recipe, not a poem. Let’s call this the pseudocode version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;! Variables used in the script
window Window
layout Content
panel Panel
label Header
panel Panel2
label Left
label Center
label Right
label Footer
menu Menu
menuitem Item

! Create a window
create Window
   at 100 100
   size 640 480
   title `My New App`

! Add the main content; a series of nested panels and labels
create in Window Content type rows

! The first row
create in Content Panel height 20%
create in Panel Header text `Header`

! The second row
create in Content Panel type columns
create in Panel Panel2
create in Panel2 Left width 20% text `Left`
create in Panel Panel2
create in Panel2 Center text `Center`
create in Panel Panel2
create in Panel2 Right width 15% text `Right`

! The third row
create in Content Panel height 15%
create in Panel Footer text `Footer`

! The menu
create in Window Menu text `File`
add to Menu Item text `About`
on click Item show dialog type info text `A sample application`
add to Menu Item text `Exit`
on click Item exit

show Window
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is 47 lines, compared to roughly 100 for the Python code that resulted from the AI query.&lt;/p&gt;

&lt;p&gt;This version has a big advantage over the natural language version. It’s compilable to a form that will run directly from the command line. This means it can be used as a prototype product, rather than being just a specification of how to build the product. That’s demonstrated by the last line, &lt;code&gt;show Window&lt;/code&gt;, which will display the window on a suitably-equipped system. Compilers and runtimes to handle this kind of script are not hard to write; in fact, something very close to this already exists in the Python world. See &lt;a href="https://github.com/easycoder/easycoder-py" rel="noopener noreferrer"&gt;EasyCoder&lt;/a&gt;. (Disclaimer: This is my own product.)&lt;/p&gt;

&lt;h2&gt;
  
  
  How does this work with AI?
&lt;/h2&gt;

&lt;p&gt;Can AI use pseudocode instead of natural language to generate a fully native application in whatever underlying language you prefer? To answer this question, I did the following:&lt;/p&gt;

&lt;p&gt;First, I submitted the original natural-language request to DeepSeek, which generated Python code. The image above is a screenshot of the running code.&lt;/p&gt;

&lt;p&gt;I then asked DeepSeek if it could recognise the pseudocode version. It came back with the confirmation that the two versions were in fact identical, and that if a suitable dictionary were built it could easily handle pseudocode instead of natural language. It described the approach as&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;a declarative UI specification system where:&lt;br&gt;
Pseudocode = Human-readable UI description&lt;br&gt;
Keyword dictionary = Mapping to actual UI components and properties&lt;br&gt;
Generator = Interprets pseudocode → creates actual UI code&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;then went on to comment that&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is similar to how many modern UI frameworks work (React, Vue components) but with an even more abstracted, human-readable syntax.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and offered the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Benefits of This Approach:&lt;br&gt;
Rapid Prototyping - Write UI structure in simple English-like syntax&lt;br&gt;
Consistency - Enforced patterns through keyword definitions&lt;br&gt;
Platform Independence - Same pseudocode could generate PySide6, Tkinter, web UI, etc.&lt;br&gt;
Non-technical Friendly - Designers can specify layouts without coding&lt;br&gt;
Version Control Friendly - Pseudocode is clean and diff-able&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Compilable pseudocode reduces complexity
&lt;/h2&gt;

&lt;p&gt;In the example above, many assumptions have been made. The way items are positioned and sized in their parent containers, the foreground and background colors, the fonts used for text, border widths and so on. In the browser world these are all handled by stylesheets, and in fact PySide handles most common stylesheet definitions. In any practical application, some form of stylesheet will have to be defined, holding global values and values that are specific to a class of on-screen widget. Doing all of this using natural language is painful to say the least, but a variant of the same pseudocode can be used to create JSON stylesheet files that can be read by the compiler. The language will need a vocabulary that allows the user to define which styles belong where, but this is not a hard task. DeepSeek offered to design such a system for me, but I’m not yet ready quite yet to dive further into this.&lt;/p&gt;

&lt;p&gt;By scripting with compilable pseudocode, products can be developed quickly with or without AI, or by combining both. If people start to use pseudocode, AI will gradually be able to provide more assistance in creating applications by examining examples of those already created.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does this change the way we work?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Unlike natural language, pseudocode scripts can be compiled on any computer and then run; hence the last line in the script shown above - &lt;code&gt;show Window&lt;/code&gt;. The entire script can be run from the command line, directly from the source text. The performance may not be sparkling, but as a means of catching errors it's unbeatable as corrections can be made instantly and the run started again. Typical compilation times are in the tens of milliseconds, even for quite large scripts. The runtime even includes a visual debugger that allows breakpoints to be added and variables examined with the application halted.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The script uses a lot of variables to represent screen elements. Some of these are reused but others hold values that are of interest for the lifetime of the application. The natural language version might name such elements, but the syntax to refer to them will be clumsy and impede understanding. Having them available as code elements allows them to be accessed at any time simply by name.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Since performance is rarely an issue with prototypes, the compiler and runtime for these scripts can check very aggressively for potential coding errors during the development phase. Typical examples are the wrong type of variable being used, or an attempt to use a variable that has no value assigned to it. Once the prototype is seen to run without errors the script can be submitted to AI for recoding in Python, JavaScript, C++ or whatever is best for the target environment. This maximises performance.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  New opportunities
&lt;/h2&gt;

&lt;p&gt;This takes us back to the title of this article - the catch-22. Pseudocode allows very large chunks of functionality to be represented by a fairly concise English syntax. New functionality, created by experts in their fields, gets added as extra pseudocode vocabulary, which is easily understood by AI and by people familiar with the domain in which they are working. It is fairly easy to take a given piece of functionality and create a new language feature for it, and it does not usually require any great knowledge of how that functionality is coded. Take Google Maps, for example, which has a remarkably simple API for such a complex and comprehensive product. We will still need highly skilled programmers to create new core functionality, but pseudocode unlocks the door to making it accessible to those with more limited skills, to build the products that depend on this functionality. This could well be where most human programmers will be employed; managing AI efficiently to create the applications needed by the world.&lt;/p&gt;

&lt;p&gt;And let’s not forget the problem of maintenance. Having advanced applications coded in pseudocode that can be understood both by AI and by humans makes future maintenance far simpler than requiring every maintenance engineer to know the details of every programming language and its libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;As AI takes over the jobs of those doing simpler forms of coding, fewer people will come up through the system and progress to the higher skill levels. The adoption of a suitable pseudocode for describing a wide range of applications will reduce the need for highly skilled coders and create a demand instead for logical thinkers who understand problem domains and can translate requirements into a form that can run as a prototype. Once this has been tested thoroughly it can then be handed to AI to generate the final code for the target environment.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>pseudocode</category>
    </item>
    <item>
      <title>OTA Python updates with ESP-Now</title>
      <dc:creator>Graham Trott</dc:creator>
      <pubDate>Sat, 27 Sep 2025 14:33:42 +0000</pubDate>
      <link>https://dev.to/gtanyware/ota-python-updates-with-esp-now-4iai</link>
      <guid>https://dev.to/gtanyware/ota-python-updates-with-esp-now-4iai</guid>
      <description>&lt;p&gt;This is the third article in a series about using ESP-Now to implement an ESP32-based master-slave network. &lt;a href="https://dev.to/gtanyware/esp-now-master-slave-in-micropython-16e2"&gt;Part one&lt;/a&gt; describes the main network comms class and &lt;a href="https://dev.to/gtanyware/channel-hopping-on-the-esp32-c3-with-esp-now-2led"&gt;part two&lt;/a&gt; describes how to follow the system router when it channel-hops. In this final article I'll describe an implementation of over-the-air (OTA) updates to the Python and other files held on the devices.&lt;/p&gt;

&lt;p&gt;In part one of this series, when a message is received it is handed to a message handler, which carries out the needed action(s) and returns a result. The following is that message handler. Most of it is directly applicable to any similar application, since there are only so many things a network slave is able to do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;binascii&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unhexlify&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;renameFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;deleteFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;createDirectory&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRelay&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;saveError&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="c1"&gt;#        print('Handler:',msg)
&lt;/span&gt;        &lt;span class="n"&gt;bleValues&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBLEValues&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OK &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUptime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; :&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bleValues&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;uptime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;on&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ON&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;No relay&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;off&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; OFF&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;No relay&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;relay&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OK &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;No relay&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;reset&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ipaddr&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OK &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIPAddr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;channel&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OK &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getChannel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;channel=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OK &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;environ&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setEnviron&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OK &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;temp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OK &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTemperature&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pause&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pause&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OK paused&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;resume&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OK resumed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;delete&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;deleteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Fail&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;part&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Format is part:{n},text:{text}
&lt;/span&gt;            &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;
            &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;part&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;unhexlify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;saveError&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;saveError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Save error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;
                        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;saveError&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
                            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Sequence error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;save&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
                &lt;span class="n"&gt;tname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Save &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tname&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
                &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
                    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                    &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="c1"&gt;# Check the file against the buffer
&lt;/span&gt;                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tname&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;File saved&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Bad save&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;No data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;update&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tname&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tname&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t-&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tname&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Update &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;deleteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;renameFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;fname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mkdir&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mkdir &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;createDirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Fail&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Unknown message: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="c1"&gt;#        print('Handler response:',response)
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;checkFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="c1"&gt;# index in lst
&lt;/span&gt;                &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="c1"&gt;# position in current list item
&lt;/span&gt;                &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="c1"&gt;# End of file: check if we've also finished the list
&lt;/span&gt;                        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
                            &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                            &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
                        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
                    &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
                        &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                        &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;OSError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;__init__()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The system makes extensive use of a &lt;code&gt;Config&lt;/code&gt; class, which holds all the system-specific configuration data such as which I/O pins do what. It also acts as a central message interchange, with functions that either return an object that has the required functionality or call the objects themselves, avoiding the need for every class to know about all the others. So in this &lt;code&gt;Handler&lt;/code&gt; class, the relay object supplied by &lt;code&gt;Config&lt;/code&gt; is a class that has the ability to turn the relay on and off and to return the current relay state.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;handleMessage()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is where most of the work is done. The function handles a set of commands, most of which should be fairly obvious, though some will be more relevant than others. For example, elsewhere in the code there's a Bluetooth (BLE) module that collects occasional transmissions from specific devices and for some message types appends these values to the string that is returned to the caller. In my system these are temperature values, but not everyone will want this feature.&lt;/p&gt;

&lt;p&gt;The most interesting part of this is the OTA file updater, which works like this. The maximum size of an ESP-Now data packet is something over 200 bytes, so few Python scripts can be transferred in a single message. I chose to send 100 bytes at a time, with the text data encoded as hex values. The file is sent as a series of messages, each containing 100 bytes, and the messages are prefixed by the command &lt;code&gt;part&lt;/code&gt; and a part number starting with zero. The function splits up the packet to extract the part number and the data itself. If the part number matches what was expected, the text is added to a list, otherwise the function returns an error. For a successfully received part, the function returns the part number and the size of the data, which the sender can use to verify that it has been received successfully.&lt;/p&gt;

&lt;p&gt;When all the parts have been sent, the list can be saved to a temporary file. This is done by the &lt;code&gt;save&lt;/code&gt; command.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;checkFile(()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Now we need to check the data has not been corrupted. This function reads the file back, one character at a time, and compares it with the contents of the list. If no errors are found, the new file can be assumed to be good.&lt;/p&gt;

&lt;p&gt;However, a problem comes when several Python files all need to be updated together. If only some of the files are updated and then an error occurs or for some other reason the update stops, there could be a mismatch between the various parts and the program will not run after the next reboot. So rather than update files one by one they should instead be done as a batch. Only when all of the files have been saved and checked is it then safe to rename the entire batch. This is done by the &lt;code&gt;update&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;In a practical implementation, when an error is discovered and reported back to the sender, the entire save can be retried. Although for the most part OTA updates proceed without a hitch, in some cases, particularly where the signal strength isn't great or suffers from interference, it can take several attempts to transfer a large file. I allow up to 10 retries before abandoning the save.&lt;/p&gt;

</description>
      <category>networking</category>
      <category>python</category>
      <category>esp32</category>
      <category>iot</category>
    </item>
    <item>
      <title>Channel-hopping on the ESP32-C3 with ESP-Now</title>
      <dc:creator>Graham Trott</dc:creator>
      <pubDate>Sat, 27 Sep 2025 14:30:21 +0000</pubDate>
      <link>https://dev.to/gtanyware/channel-hopping-on-the-esp32-c3-with-esp-now-2led</link>
      <guid>https://dev.to/gtanyware/channel-hopping-on-the-esp32-c3-with-esp-now-2led</guid>
      <description>&lt;p&gt;This is the second article in a series about using ESP-Now in Micropython to implement an ESP32-based master-slave network. &lt;a href="https://dev.to/gtanyware/esp-now-master-slave-in-micropython-16e2"&gt;Part one&lt;/a&gt; describes the main network comms class and &lt;a href="https://dev.to/gtanyware/ota-python-updates-with-esp-now-4iai"&gt;part three&lt;/a&gt; describes how to do reliable OTA file updates. &lt;/p&gt;

&lt;p&gt;The 2.4GHz wifi band is getting crowded. Much of the heaviest traffic now goes on 5GHz, but that leaves a lot of devices competing for bandwidth; in particular those in the world of IOT. Modern domestic routers deal with this by channel-hopping; they periodically monitor the network to see which channel is the least busy and switch to that channel. This can happen as frequently as every hour or two, but I would hope that routers are intelligent enough to realise that constantly ping-ponging back and forth would be mostly ineffective as well as annoying. For although computer operating systems can deal pretty seamlessly with channel-hopping, simpler devices don't have the firmware to do this, leaving you and me to do it in our applications.&lt;/p&gt;

&lt;p&gt;The simplest and most recommended approach is of course to restrict the router to a single channel, but this is only feasible if you have control of the router. For my master-slave system I made no such assumption; I decided to grab the bull by the horns and work out a solution.&lt;/p&gt;

&lt;p&gt;Before continuing, I should mention that this code has been developed for the ESP32-C3, which is a cut-down device with only a single radio subsystem. This means it can only handle a single wifi channel. Other variants have two subsystems and can operate two channels independently. The code here and in the rest of the series is built to handle the more constrained environment. During development I found that many published articles about ESP-Now on the ESP32 don't work on the C3 variant as they rely on having the dual-radio.&lt;/p&gt;

&lt;p&gt;In fact, with dual radio devices there may well be no need to channel-hop at all. The entire ESP-Now subsystem can operate independently of the AP and STA interfaces. However, the C3 variant occupies a niche that was previously filled by the ESP8266; low cost and very small size, as exhibited by devices such as the ESP32-C3 Super Mini and the ESP01-C3, allowing a full implementation of Micropython to run many cost and space-critical applications. Here's a ESP32-C3 Super Mini (with antenna) and an ESP01-C3.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwf4afc3bgkawn3mfnfcu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwf4afc3bgkawn3mfnfcu.jpg" alt="ESP32-C3 Supermini and ESP01-C3" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So if you're still with me, here's the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Channels&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Starting Channels&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;myMaster&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMyMaster&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isMaster&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSSID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPassword&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkRouterChannel&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resetCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setupSlaveTasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Set up slave tasks&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMyMaster&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;countMissingMessages&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resetCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;idleCount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;findMyMaster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hopToNextChannel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_event_loop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromhex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;myMaster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;espSend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ping&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Ping response from&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;myMaster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Found master on channel&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;countMissingMessages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Count missing messages&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;
        &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ap&lt;/span&gt;
        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;idleCount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;idleCount&lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;idleCount&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;No messages for 30 seconds&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="c1"&gt;# Retry the current channel
&lt;/span&gt;                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;idleCount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
                    &lt;span class="k"&gt;continue&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Master not found on channel &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hopToNextChannel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_event_loop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hopToNextChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channels&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channels&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channels&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
                &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;==-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;checkRouterChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Check the router channel&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;sta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sta&lt;/span&gt;
            &lt;span class="n"&gt;sta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Reconnecting...&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;sta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;sta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isconnected&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;channel&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; router changed channel from&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_event_loop&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; no channel change&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;espComms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restartESPNow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this system, the &lt;code&gt;ESPComms&lt;/code&gt; class described previously manages all network functions except for the channel hopping, which is done by this &lt;code&gt;Channels&lt;/code&gt; class. The system also makes heavy use of a &lt;code&gt;Config&lt;/code&gt; class, which manages system data such as SSIDs and passwords, I/O pin usage and so on. It also acts as a central routing point for most communications between other modules, having getter and setter functions that just pass on calls to the appropriate class. &lt;code&gt;Channels&lt;/code&gt; is something of an exception as it's tied quite closely to &lt;code&gt;ESPComms&lt;/code&gt;, so most of the function calls in this class go there directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;__init__()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This defines the channels to be used. Although there are about 14 distinct channels in the 2.4GHz band, they are close enough together that adjacent ones overlap quite seriously. In fact, there are only three completely non-overlapping channels: 1, 6 and 11, so it will come as no surprise that most routers hop between these three. The function defines these channels. Its other job is to start up an asynchronous job (&lt;code&gt;checkRouterChannel()&lt;/code&gt;, see below) to monitor the system and detect when the channel has changed. This is needed when the system acts as the master device, as otherwise the change of channel might go undetected.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;setupSlaveTasks()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is typically called later during initialization, after other things have settled down.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;resetCounter()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The class constantly increments a counter, and when it reaches a predefined limit, action must be taken. This function is called from &lt;code&gt;ESPComms&lt;/code&gt; whenever a message is received, to prevent the action from being triggered.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;findMyMaster()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When a slave starts up it has no way of knowing which channel its master device is using. So it calls this function, which issues a 'ping' message on the channel that was saved during the last run. If it gets a reply it knows it's picked the right channel. Otherwise, it hops to the next channel, thereby saving the new channel number, and resets itself. Only once it gets a reply to the ping can it break out of this endless cycle and start normal operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;countMissingMessages()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This function runs continuously, incrementing the counter and checking if it has exceeded its set limit. If so, it first pings the master to see if this is still awake and on the same channel. There may be a perfectly valid reason for missing messages, such as the system being in maintenance mode where normal operations are suspended. If the ping fails, the device will reboot and re-enter the &lt;code&gt;findMyMaster()&lt;/code&gt; cycle above.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ping()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This sends a 'ping' message to the master device and waits for a reply, returning &lt;code&gt;True&lt;/code&gt; if it gets one within a second. To save you having to look it up, the function &lt;code&gt;espSend()&lt;/code&gt; in &lt;code&gt;ESPComms&lt;/code&gt; is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;espSend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPeer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;espSend:&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;countMissingMessages()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This continually increments &lt;code&gt;idleCount&lt;/code&gt; and checks if it has reached its predefined limit. If so, messages have gone missing, so it hops to the next channel and resets.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;checkRouterChannel()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is called periodically - say every 5 minutes - by the system master to ensure it is still on the same channel as the router. To do this, the STA interface must be deactivated and reconnected. If the channel has changed, a reboot is needed, otherwise ESP-Now can be restarted.&lt;/p&gt;

</description>
      <category>iot</category>
      <category>networking</category>
      <category>python</category>
      <category>esp32</category>
    </item>
    <item>
      <title>ESP-Now master-slave in Micropython</title>
      <dc:creator>Graham Trott</dc:creator>
      <pubDate>Sat, 27 Sep 2025 14:25:23 +0000</pubDate>
      <link>https://dev.to/gtanyware/esp-now-master-slave-in-micropython-16e2</link>
      <guid>https://dev.to/gtanyware/esp-now-master-slave-in-micropython-16e2</guid>
      <description>&lt;p&gt;This article describes how to build a master-slave network of ESP32 devices using Micropython and ESP-Now.&lt;/p&gt;

&lt;p&gt;ESP-Now is a simple low-level protocol for communicating between ESP32-based devices independently of HTTP. Many articles have been written on the subject, but the great majority cover simple scenarios, and once things get more complex these simple techniques start to reveal their limitations. This article is the result of several weeks of trial and error, with extensive help from DeepSeek, which patiently sought out the answers to a seemingly endless series of baffling questions.&lt;/p&gt;

&lt;p&gt;[Note: When I asked DeepSeek how best to make code available to a wider audience, it encouraged me to write this article. Forgive me for anthropomorphising slightly, but AI engines appear to enoy reading articles that describe working code. They also examine code repositories, but it seems they give greater weight to those that are supported by articles aimed at a technical readership.]&lt;/p&gt;

&lt;p&gt;The system in question comprises a single master (hub) device that acts as a gateway to an indefinite number of slaves arranged as a star network. In a &lt;a href="https://dev.to/gtanyware/using-subnets-to-extend-esp-now-mpb"&gt;previous article&lt;/a&gt;, I described how to extend such a network by arranging for slaves to run their own local networks, which overcomes the problem of wifi range and also of the number of peers that any one node can support. The code I presented was somewhat simplistic and proved to have a number of limitations, so here I describe a revised class that deals with the problems unearthed during the development process.&lt;/p&gt;

&lt;p&gt;I'll start by presenting the code, then I'll describe its most important features:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;machine&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;espnow&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ESPNow&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ESPComms&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;ESPNow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isMaster&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Starting as master&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;WLAN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WLAN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IF_STA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;ssid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSSID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPassword&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ssid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Connecting...&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ssid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isconnected&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Timeout&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearAndReset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;ipaddr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ifconfig&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;channel&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setIPAddr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ipaddr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ipaddr&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ch &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getChannel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Starting as slave on channel&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;WLAN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AP_IF&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mac&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setMAC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;RBR-Now-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;essid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;00000000&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ifconfig&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;192.168.9.1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;255.255.255.0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;192.168.9.1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;8.8.8.8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ap&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isMaster&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;WLAN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WLAN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IF_STA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ESP-Now initialised&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;requestToSend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sending&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;closeAP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;999999&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Password:&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;essid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;addPeer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;peers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;OSError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Failed to add peer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; to ESP-NOW: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Added&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;to peers on channel&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;espSend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPeer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;espSend:&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="c1"&gt;#        print(f'Send {msg[0:20]}... to {mac} on channel {self.channel}')
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;requestToSend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;requestToSend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromhex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPeer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;
                    &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;
                    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                            &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;irecv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                                &lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ping&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;span class="c1"&gt;#                                print(f"Received reply: {reply}")
&lt;/span&gt;                                &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;
                                &lt;span class="k"&gt;break&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;
                        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;-=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Fail (no reply)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resetCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Fail (no result)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Fail (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Fail (adding peer)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sending&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Starting ESPNow receiver&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                    &lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Received&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ping&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPeer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pong&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ping:&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                            &lt;span class="c1"&gt;# It's a message to be relayed
&lt;/span&gt;                            &lt;span class="n"&gt;comma&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="n"&gt;slave&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;comma&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                            &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;comma&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
&lt;span class="c1"&gt;#                            print(f'Slave: {slave}, msg: {msg}')
&lt;/span&gt;                            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slave&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                            &lt;span class="c1"&gt;# It's a message for me
&lt;/span&gt;                            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHandler&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPeer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPeer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resetCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMyMaster&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isMaster&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setMyMaster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Can&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s"&gt;t respond&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;requestToSend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sending&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
                    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Not active&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kickWatchdog&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getRSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peers_table&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;restartESPNow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;ESPNow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The job of this class is to deliver small packets of data to specified MAC addresses and to return results back to the caller. As ESP-Now is a low-level protocol it does not deal with IP addresses, access point names or any of the other higher layers of the OSI model.&lt;/p&gt;

&lt;p&gt;ESP-Now requires the device to be already set up in either Access Point or Station mode. Its communications go unnoticed by higher-level functions such as HTTP, so it can run concurrently with them.&lt;/p&gt;

&lt;p&gt;In this class, the master (hub) device connects to the house router with its station (STA) interface but the slaves do not (they still however require STA to be active for ESP-Now to work). Both master and slaves use the same code modules but in different ways. In both cases, an access point (AP) is set up for the first 2 minutes following a reboot, so that an administrator can talk directly to the device over-the-air from a local browser. This feature is used for configuration, mostly avoiding the need for ESP32 modules to be physically extracted for code alterations.&lt;/p&gt;

&lt;p&gt;In this implementation, all messages, commands and requests are initiated outside of the ESP-Now network and handed to an HTTP server maintained by the master for onward transmission. Slave devices do not initiate messages other than the occasional 'ping' (see below). Adding full two-way messaging would result in considerable extra complexity, and all the necessary features can usually be provided without it.&lt;/p&gt;

&lt;p&gt;The code here was developed with a lot of trial-and-error and the assistance of AI to find relevant examples. There turned out to be none that directly considered the complexities of the scenario; most are far simpler. If anyone else has solved the same problem, they hadn't told the world about it at the time this code development was underway.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;__init__()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The class makes use of a second module, &lt;code&gt;Config&lt;/code&gt;, which handles system parameters such as router SSID/password and identifies which I/O pins are used by the device. This data is typically held in a JSON file that can be read and written as necessary. Wherever you see &lt;code&gt;config.xxxxx()&lt;/code&gt;, a function in the config module is being called.&lt;/p&gt;

&lt;p&gt;ESP-Now is extremely fussy about configuration. There may be differences between the variants of ESP32; this code is known to work on ESP32-C3, which is a fairly basic model, so it should be fine on most other variants. There is a strict sequence to follow, which differs between master and slave.&lt;/p&gt;

&lt;p&gt;The master first requires STA to be active. Here it is connected to the house router, which governs the wifi channel the system will use. Then the AP is set up, and finally ESP-Now is activated. This ordering guarantees that ESP-Now will use the STA wifi channel.&lt;/p&gt;

&lt;p&gt;The slave does things the other way round, first setting up AP and following that with a stub STA. This ensures that the AP channel will be used for ESP-Now communications. If you don't get this order right, some things will probably not work and strange errors will arise.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;closeAP()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;During the first 2 minutes following boot, the AP can be connected from a browser and will accept requests and commands. These include rewriting the JSON config file and rebooting the device. After this period, the SSID of the AP is changed to &lt;code&gt;-&lt;/code&gt; and a random password is applied, making it almost impossible to discover and connect to. This is a security feature that prevents malicious interference unless the attacker can physically repower the device.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;addPeer()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;It's very easy to get ESP-Now peer management wrong. The published documentation gives few hints as to how fussy the protocol is about handling peers. For example, it fails to mention that a channel change not only invalidates the peers table but makes it impossible to recreate. Only a reset will do that.&lt;/p&gt;

&lt;p&gt;Although ESP-Now will happily receive messages from anywhere, sending can only be done to a peer whose MAC address has been registered with &lt;code&gt;e.add_peer()&lt;/code&gt;. This must be done just once for any given peer or an error will be thrown. So this function manages its own peers list to track what has already been added. Note that the device is likely to have 2 MAC addresses; one for each of its 2 interfaces. When you initiate a send it will usually be to one of these, but when replying to a message it will be the other. Both must be added to the peers table.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;send()&lt;/code&gt; and &lt;code&gt;receive()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;receive()&lt;/code&gt; function runs asynchronously in a loop, waiting for messages and replying to them. The original version of this class allowed messages to be sent without any consideration of what might happen if both activities try to take place at the same time. In a simple situation this will often work, but it's extremely vulnerable. One solution that appeared in the AI searches was to use a lock, so that send and receive can be exclusive, but this gives rise to some horrible issues that are hard to track down. The strategy here is one that avoids such issues and is much easier to debug.&lt;/p&gt;

&lt;p&gt;In the receive loop, when a message arrives it is passed to the &lt;code&gt;handleMessage()&lt;/code&gt; function in a companion &lt;code&gt;Handler&lt;/code&gt; class, which returns a reply to send back to the caller. While this is happening, a &lt;code&gt;sending&lt;/code&gt; flag is set &lt;code&gt;False&lt;/code&gt;. Any request to send will cause the sender to raise &lt;code&gt;requestToSend&lt;/code&gt; and block until &lt;code&gt;sending&lt;/code&gt; goes &lt;code&gt;True&lt;/code&gt;, which is done at the bottom of the receive loop. The receive function can tell from &lt;code&gt;requestToSend&lt;/code&gt; that the sender is waiting. The function can now release &lt;code&gt;sending&lt;/code&gt; to allow the message to be sent, and it then waits for that process to finish.&lt;/p&gt;

&lt;p&gt;However, a received message might not have come from the device we just sent to. This system is designed to handle router channel changes, and when this happens the master quickly changes to the new channel, but all the slaves know is that messages have stopped arriving (because they're all on the wrong channel). So after a while they issue a 'ping' message to the master. If it replies they're on the right channel, otherwise they change channels and try the ping again.&lt;/p&gt;

&lt;p&gt;The result is that one or more 'ping' messages can arrive while the sender is waiting for a reply to its own message, so the code watches out for them and discards them. Eventually it sees its own reply message and returns it to the caller, but before doing so it clears &lt;code&gt;sending&lt;/code&gt;, which releases the receiver to look out for more incoming messages.&lt;/p&gt;

&lt;p&gt;Note that this code cannot deal with anything but 'ping' messages from its slaves. Any scenario requiring a more general 2-way send and receive will probably need to use some form of addressing to identify message senders.&lt;/p&gt;

&lt;p&gt;The code in &lt;code&gt;receive()&lt;/code&gt; has one more feature. If the received message starts with &lt;code&gt;!&lt;/code&gt; it's not for this device, but instead is a request to forward a message to another device. The MAC address of the intended recipient follows the &lt;code&gt;!&lt;/code&gt; and is terminated by a comma. The code strips these away and is left with a MAC address and a (shorter) message, which it forwards, returning whatever returns back up to its own caller.&lt;/p&gt;

&lt;h2&gt;
  
  
  Channel changes
&lt;/h2&gt;

&lt;p&gt;Because the 2.4Ghz wifi band is often quite crowded, most modern wifi routers run a background task that looks out for the least busy channel. Every so often, typically at intervals of several hours, the router will then change to this quieter channel. PC operating systems are aware of this behaviour and engage in a dialog with the router to get advance notice of an upcoming change, so the effect is barely noticeable by the users of these machines. However, simple IOT devices are not generally equipped to deal with channel changes; all they know is that messages stop arriving. To avoid the need to restrict a router to a single channel, the &lt;code&gt;Channels&lt;/code&gt; class works with the code above to deal with this and other related issues, and this is described in &lt;a href="https://dev.to/gtanyware/channel-hopping-on-the-esp32-c3-with-esp-now-2led"&gt;part two&lt;/a&gt; of this series. &lt;a href="https://dev.to/gtanyware/ota-python-updates-with-esp-now-4iai"&gt;Part three&lt;/a&gt; describes the message handler, which includes a strategy for reliably updating Python modules over the air.&lt;/p&gt;

&lt;p&gt;Full code and documentation for the project can be found at &lt;a href="https://github.com/easycoder/rbr" rel="noopener noreferrer"&gt;https://github.com/easycoder/rbr&lt;/a&gt;. The Micropython code is in the &lt;code&gt;RBRNow&lt;/code&gt; folder. I may be able to keep the code above in step with changes as they happen, but it would be better to use the repository version.&lt;/p&gt;

</description>
      <category>iot</category>
      <category>networking</category>
      <category>python</category>
      <category>esp32</category>
    </item>
    <item>
      <title>An AI virtual keyboard</title>
      <dc:creator>Graham Trott</dc:creator>
      <pubDate>Mon, 12 May 2025 15:30:49 +0000</pubDate>
      <link>https://dev.to/gtanyware/an-ai-virtual-keyboard-d0f</link>
      <guid>https://dev.to/gtanyware/an-ai-virtual-keyboard-d0f</guid>
      <description>&lt;p&gt;I only recently overcame my usual inertia and responded to the invitations by VSCode to start using Copilot. Since then I've spent quite some time refining a fairly complex prompt, so I've decided to share my experience with the community.&lt;/p&gt;

&lt;p&gt;I operate in the world of Python. In this world there really is no excuse for not using a graphical interface wherever one is appropriate. It's really easy to get Copilot to create a UI to accept input and present output, though a little harder to produce something that's also aesthetically pleasing. But there's a point at which a prompt takes longer to write than does coding the entire job manually. &lt;/p&gt;

&lt;p&gt;The project I'm working on is a home heating control system, the control code and the user interface for which are both written in Python, the latter with some help from Copilot. The controller hardware is a 7-inch industrial computer from Ali Express, which arrived with a dreadful Android implementation but which I successfully reflashed to Debian Linux. Now it's a proper computer, but it lacks an essential feature; there's no virtual keyboard. (There seem to be a couple of truly appalling ones out there, but I wouldn't inflict them on a customer.)&lt;/p&gt;

&lt;p&gt;So the need was for a Python virtual keyboard. Quite a tedious manual programming task, but I figured it would be ideal for Copilot to get its teeth into. I decided to emulate an Android keyboard, which has 4 views that switch when shift or mode keys are tapped, and above the keys there's a text field for the characters being typed. Here's the first view of the keyboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0n11cctxqr4dujiydd9e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0n11cctxqr4dujiydd9e.png" alt=" " width="609" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;'Prompt engineering' is the term that refers to the skill needed to get effective results from an AI tool. As with most things in life, the GIGO principle applies; Garbage In, Garbage Out. I found it necessary to apply a good deal of guidance or the results had severe limitations and were a poor starting point for further manual embellishment. But if you're careful with your prompts you can get quite decent Python code from an AI engine. &lt;/p&gt;

&lt;p&gt;I say "can" because there's a massive caveat coming. A keyboard is not an exceptionally complex item, but without a lot of guidance all the AI engines I tried produced poorly structured code that didn't behave properly. So I tried again, this time leveraging my existing knowledge of Python and PySide (the Qt graphics library module) to structure my prompt. I requested a series of classes - a keyboard button, a row of buttons, a keyboard view comprising several rows, and finally the keyboard itself - giving for each class a set of function definitions and letting Copilot fill in the details. It turned out to be quite a substantial task. In fact, the prompt - even though it's incomplete - is far longer than the final working Python code - somewhere around 500 lines versus 320 lines of Python.&lt;/p&gt;

&lt;p&gt;One problem is that human-computer interfaces are a lot more complex that might be imagined, and a lot of details have to be spelled out in, well, excruciating detail. That's a job for a computer program, of course, which begs the question of what kind of language we should be using to write AI prompts. It's early days for this technology, but I feel there's a Eureka! moment waiting to be unearthed.&lt;/p&gt;

&lt;p&gt;Sadly, the rather long prompt below will not create a complete keyboard. For example, there's no text box to receive the typed characters. And if your experience is anything like mine, you won't get a complete set of keyboard views; Copilot just generates a comment that views 1, 2 and 3 are similar to view 0 and invites you to do the rest yourself. It seems to be influenced by previous versions of your prompt and decides to ignore bits of the new one. However, what you do get is probably enough to make completing the job by hand relatively easy.&lt;/p&gt;

&lt;p&gt;And that's my main take-away from this exercise. There are a lot of details that are simply too tedious to cover in an all-encompassing prompt script. You're far better off generating the main outline then using smaller prompts to fill in the nit-picky little details.&lt;/p&gt;

&lt;p&gt;The bottom line is, AI isn't going to take away your job and do it better. Not yet, anyway. But it can be a most valuable assistant, so please start using it. And your comments are welcome - I'm still at the start of the learning curve.&lt;/p&gt;

&lt;p&gt;(Cover image by Gemini)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This prompt describes a virtual keyboard,
to be written in Python using Pyside6 graphics.
I provide Python-style class definitions,
with constructor and function prototypes.
Python comments are general notes and instructions
to help build the classes.

#---------------------------------------------------------------

class KeyboardButton(QPushButton):
    def __init__(self, width, height, callback, text, icon):

# The text to be shown on the button can be any string or None
# The icon to be shown on the button can be any graphic, or None
# The button has rounded corners of radius 20% of its height
# The font size is half the button height
# The background of the button is white
# Apply a light shadow to the right and bottom of the button
# When the button is clicked it moves 2 pixels down and right,
# waits 200ms then moves back to its original position.
# On click, call function 'callback' with the value of 'text'.

#---------------------------------------------------------------

class KeyboardRow(QHBoxLayout):
    def __init__(self, list):  # add the items in 'list'

#---------------------------------------------------------------

class KeyboardView(QVBoxLayout):
    def __init__(self, list):  # add the items in 'list'

#---------------------------------------------------------------
This class is a complete keyboard with 4 views,
which are added one by one and only one of which can be visible.

class VirtualKeyboard(QStackedWidget):
    def __init__(self, buttonHeight):
        super().__init__()
        self.buttonHeight = buttonHeight
    def addView(self, view): # Add 'view' to the VirtualKeyboard
    def getCurrentView(self): # return the current index
    def onClickChar(self, keycode): # Print the key code
    def onClickShift(self, keycode): # Print 'Shift'.
        # if currentView is 0 call AbstractKeyboard.setCurrentIndex(1)
        # elif currentView is 1 call AbstractKeyboard.setCurrentIndex(0)
    def onClickNumbers(self, keycode):  # Print `Numbers`
        # call AbstractKeyboard.setCurrentIndex(2)
    def onClickLetters(self, keycode):  # Print `Letters`
        # call AbstractKeyboard.setCurrentIndex(3)
    def onClickBack(self, keycode):  # Print 'Back'
    def onClickSpace(self, keycode):  # Print `Space`
    def onClickEnter(self, keycode):  # Print 'Enter'

# This class sets up a keyboard

# __init__() calls each of the addKeyboardLayout() functions

The 4 keyboard views are as follows.
All buttons have the height given by buttonHeight.
All buttons have the same width as their height
unless specified otherwise.

#---------------------------------------------------------------
# View 0

Create an empty row list

Create a list of KeyboardButton with the characters in 'qwertyuiop'
The 'callback' function is onClickChar
Create a KeyboardRow with this list
Add the KeyboardRow to the row list

Create an empty list of widgets
Add a horizontal stretch to the widget list
Create a list of KeyboardButton with the characters into 'asdfghjkl'
The 'callback' function is onClickChar
Append this list to the widget list
Add a horizontal stretch to the widget list
Create a KeyboardRow with the widget list
Add the KeyboardRow to the row list

Create an empty list of widgets
Create a KeyboardButton with
    a width 150% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'up.png'
    the 'callback' function is onClickShift
Append the KeyboardButton to the widget list
Append a spacer whose width is 5% of the standard height
Create a list of KeyboardButton with the characters in 'zxcvbnm'
The 'callback' function is onClickChar
Append this list to the widget list
Append a spacer whose width is 5% of the standard height
Create a KeyboardButton with
    a width 150% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'back.png'
    the 'callback' function is onClickBack
Append the KeyboardButton to the widget list
Create a KeyboardRow with the widget list
Add the KeyboardRow to the row list

Create an empty list of widgets
Create a KeyboardButton with
    a width 150% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'numbers.png'
    the 'callback' function is onClickNumbers
Append the KeyboardButton to the widget list
Append a spacer whose width is 5% of the standard height
Create a KeyboardButton with the character ','
The 'callback' function is onClickChar
Append this button to the widget list
Create a KeyboardButton with
    a width 500% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'space.png'
    the 'callback' function is onClickSpace
Create a KeyboardButton with the character '.'
The 'callback' function is onClickChar
Append this button to the widget list
Append a spacer whose width is 5% of the standard height
Create a KeyboardButton with
    a width 150% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'enter.png'
    the 'callback' function is onClickEnter
Append the KeyboardButton to the widget list
Create a KeyboardRow with the widget list
Add the KeyboardRow to the row list

Create a KeyboardView with the row list
Add the KeyboardView to this VirtualKeyboard

#---------------------------------------------------------------
# View 1

Create an empty row list

Create a list of KeyboardButton  with the characters in 'QWERTYUIOP'
The 'callback' function is onClickChar
Create a KeyboardRow with this list
Add the KeyboardRow to the row list

Create an empty list of widgets
Add a horizontal stretch to the widget list
Create a list of KeyboardButton  with the characters in 'ASDFGHJKL'
The 'callback' function is onClickChar
Append this list to the widget list
Add a horizontal stretch to the widget list
Create a KeyboardRow with the widget list
Add the KeyboardRow to the row list

Create an empty list of widgets
Create a KeyboardButton with
    a width 150% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'up.png'
    the 'callback' function is onClickShift
Append the KeyboardButton to the widgt list
Append a spacer whose width is 5% of the standard height
Create a list of KeyboardButton with the characters in 'ZXCVBNM'
The 'callback' function is onClickChar
Append this list to the widget list
Append a spacer whose width is 5% of the standard height
Create a KeyboardButton with
    a width 150% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'back.png'
    the 'callback' function is onClickBack
Append the KeyboardButton to the widget list
Create a KeyboardRow with the widget list
Add the KeyboardRow to the row list

Create an empty list of widgets
Create a KeyboardButton with
    a width 150% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'numbers.png'
    the 'callback' function is onClickNumbers
Append the KeyboardButton to the widget list
Append a spacer whose width is 5% of the standard height
Create a KeyboardButton with the character ','
The 'callback' function is onClickChar
Append this button to the widget list
Create a KeyboardButton with
    a width 500% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'space.png'
    the 'callback' function is onClickSpace
Create a KeyboardButton with the character '.'
The 'callback' function is onClickChar
Append this button to the widget list
Append a spacer whose width is 5% of the standard height
Create a KeyboardButton with
    a width 150% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'enter.png'
    the 'callback' function is onClickEnter
Append the KeyboardButton to the widget list
Create a KeyboardRow with the widget list
Add the KeyboardRow to the row list

Create a KeyboardView with the row list
Add the KeyboardView to this VirtualKeyboard

#---------------------------------------------------------------
# View 2

Create an empty row list

Create a list of KeyboardButton  with the characters in '1234567890'
The 'callback' function is onClickChar
Create a KeyboardRow with this list
Add the KeyboardRow to the row list

Create a list of KeyboardButton  with the characters in '@#£&amp;amp;_-()=%'
The 'callback' function is onClickChar
Create a KeyboardRow with this list
Add the KeyboardRow to the row list

Create an empty list of widgets
Create a KeyboardButton with
    the standard height
    a text value None
    an icon taken from the graphic 'symbols.png'
    the 'callback' function is onClickSymbols
Append the KeyboardButton to the widgt list
Append a spacer whose width is 5% of the standard height
Create a list of KeyboardButton with the characters in '"*'
Add "'" to the list
Add the characters in ':/!?+' to the list
The 'callback' function for everything in this list is onClickChar
Append this list to the widget list
Append a spacer whose width is 5% of the standard height
Create a KeyboardButton with
    the standard height
    a text value None
    an icon taken from the graphic 'back.png'
    the 'callback' function is onClickBack
Append the KeyboardButton to the widget list
Create a KeyboardRow with the widget list
Add the KeyboardRow to the row list

Create an empty list of widgets
Create a KeyboardButton with
    a width 150% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'letters.png'
    the 'callback' function is onClickLetters
Append the KeyboardButton to the widget list
Append a spacer whose width is 5% of the standard height
Create a KeyboardButton with
    a width 500% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'space.png'
    the 'callback' function is onClickSpace
Create a KeyboardButton with the character '.'
The 'callback' function is onClickChar
Append this button to the widget list
Append a spacer whose width is 5% of the standard height
Create a KeyboardButton with
    a width 150% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'enter.png'
    the 'callback' function is onClickEnter
Append the KeyboardButton to the widget list
Create a KeyboardRow with the widget list
Add the KeyboardRow to the row list

Create a KeyboardView with the row list
Add the KeyboardView to this VirtualKeyboard

#---------------------------------------------------------------
# View 3

Create an empty row list

Create a list of KeyboardButton  with the characters in '$€¥¢©®µ~¿¡'
The 'callback' function is onClickChar
Create a KeyboardRow with this list
Add the KeyboardRow to the row list

Create a list of KeyboardButton  with the characters in '¼½¾[]{}&amp;lt;&amp;gt;^'
The 'callback' function is onClickChar
Create a KeyboardRow with this list
Add the KeyboardRow to the row list

Create an empty list of widgets
Create a KeyboardButton with
    the standard height
    a text value None
    an icon taken from the graphic 'numbers.png'
    the 'callback' function is onClickNumbers
Append the KeyboardButton to the widget list
Append a spacer whose width is 5% of the standard height
Create a list of KeyboardButton with the characters in '`;÷\∣|¬±'
The 'callback' function is onClickChar
Append this list to the widget list
Append a spacer whose width is 5% of the standard height
Create a KeyboardButton with
    the standard height
    a text value None
    an icon taken from the graphic 'back.png'
    the 'callback' function is onClickBack
Append the KeyboardButton to the widget list
Create a KeyboardRow with the widget list
Add the KeyboardRow to the row list

Create an empty list of widgets
Create a KeyboardButton with
    the standard height
    a text value None
    an icon taken from the graphic 'symbols.png'
    the 'callback' function is onClickSymbols
Append the KeyboardButton to the widgt list
Append a spacer whose width is 5% of the standard height
Create a list of KeyboardButton with the characters in '"*'
Add "'" to the list
Add the characters in ':/!?+' to the list
The 'callback' function for everything in this list is onClickChar
Append this list to the widget list
Append a spacer whose width is 5% of the standard height
Create a KeyboardButton with
    the standard height
    a text value None
    an icon taken from the graphic 'back.png'
    the 'callback' function is onClickBack
Append the KeyboardButton to the widget list
Create a KeyboardRow with the widget list
Add the KeyboardRow to the row list

Create an empty list of widgets
Create a KeyboardButton with
    a width 150% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'letters.png'
    the 'callback' function is onClickLetters
Append the KeyboardButton to the widget list
Append a spacer whose width is 5% of the standard height
Create a KeyboardButton with the character ','
The 'callback' function is onClickChar
Append this button to the widget list
Create a KeyboardButton with
    a width 300% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'space.png'
    the 'callback' function is onClickSpace
Create a KeyboardButton with the character '.'
The 'callback' function is onClickChar
Append this button to the widget list
Append a spacer whose width is 5% of the standard height
Create a list of KeyboardButton with the characters in '✕§¶°'
The 'callback' function is onClickChar
Append this list to the widget list
Create a KeyboardButton with
    a width 150% of the standard height
    the standard height
    a text value None
    an icon taken from the graphic 'enter.png'
    the 'callback' function is onClickEnter
Append the KeyboardButton to the widget list
Create a KeyboardRow with the widget list
Add the KeyboardRow to the row list

Create a KeyboardView with the row list
Add the KeyboardView to this VirtualKeyboard

#---------------------------------------------------------------

This is a test program:

Create a window 600 pixels wide and 350 high with background #ccc.
Compute a standard button height value, as 15% of the window height. 
Create a VirtualKeyboard, giving it the computed height
Add the VirtualKeyboard to the window
Show the window

Note: QGraphicsDropShadowEffect is part of QtWidgets, not QtCore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here are the control key graphics:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe4cb6q2g96n9cfwfqn54.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe4cb6q2g96n9cfwfqn54.png" alt=" " width="716" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Using subnets to extend ESP-Now</title>
      <dc:creator>Graham Trott</dc:creator>
      <pubDate>Sat, 19 Apr 2025 16:03:45 +0000</pubDate>
      <link>https://dev.to/gtanyware/using-subnets-to-extend-esp-now-mpb</link>
      <guid>https://dev.to/gtanyware/using-subnets-to-extend-esp-now-mpb</guid>
      <description>&lt;p&gt;ESP-Now is a lightweight networking protocol that uses only two layers (Physical and Data Link) of the OSI model. By doing so it offers high performance and simple programming.&lt;/p&gt;

&lt;p&gt;In a typical configuration, a single hub device maintains an HTTP connection to the system controller, either as a client or a server, and uses ESP-Now to communicate directly with up to 20 other devices. Commands and queries arrive at the hub on the HTTP interface and are routed to the appropriate device. These messages are sent using a call to a library function that takes 2 arguments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; The MAC address of the intended recipient&lt;/li&gt;
&lt;li&gt; The payload, which can be up to 200 bytes. (A new version increases this limit but I'll ignore that for the purposes of this article.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a small network, the system offers great performance, impressive reliability and easy programming, but it has two fundamental limitations, being the 20 devices as stated above, and the distance from the hub to the furthest device. As with all things wifi, range is an issue. It can be very hard to find a location for a wifi hub where all of the devices to be controlled can see it reliably. Here's view of a hub with 4 connected devices:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl8u8tmwnakodhg4ezr11.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl8u8tmwnakodhg4ezr11.png" alt="An ESP-Now system" width="513" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The red hatched area is outside the wifi range of the hub, so if new devices are added here they will suffer from poor connections and will consequently be unreliable:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7l47dfcxyfeak2b73ay9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7l47dfcxyfeak2b73ay9.png" alt="Some distant devices" width="691" height="650"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;One well-known solution to this is to implement a mesh, but this functionality is not offered as part of the ESP-Now spec. In any case, a mesh is often overkill, since what is usually needed is for the hub to drive more devices over a greater range, not for all the devices to talk to each other in arbitrary ways.&lt;/p&gt;

&lt;p&gt;Fortunately, there's a quite simple solution to this problem, which is to allow any device to handle devices on a subnet of its own. The device count and the range of the network both immediately increase dramatically, albeit with a performance hit, but one that is likely to be insignificant for most small control systems:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdvysp2kllylg3o37rpjc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdvysp2kllylg3o37rpjc.png" alt="Extending the network" width="691" height="650"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The way it works is like this. Every device in the system is described by a packet of information, typically a JSON structure, with fields such as the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"name" - the unique name of the device in the system
&lt;/li&gt;
&lt;li&gt;"mac" - the MAC address of the device
&lt;/li&gt;
&lt;li&gt;"channel" - the wifi channel used
&lt;/li&gt;
&lt;li&gt;"led" - the pin that drives the device LED
&lt;/li&gt;
&lt;li&gt;"led-invert" - a Boolean that governs the ON and OFF polarity
&lt;/li&gt;
&lt;li&gt;"relay" - the pin that drives the device's primary relay
&lt;/li&gt;
&lt;li&gt;"relay-invert" - a Boolean that governs the ON and OFF polarity
&lt;/li&gt;
&lt;li&gt;... (etc for other hardware and software features of this device)
&lt;/li&gt;
&lt;li&gt;"path" - the path to this device&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each device has a JSON file containing its own information, and the central controller holds a file containing all the device structures, indexed by the device names.&lt;/p&gt;

&lt;p&gt;The last item in the list is the key to the extended networking. For devices close to the hub, nothing is needed here as messages are sent using the MAC address of the device and the payload. But by placing the MAC address of an intermediate device into the field, that device then becomes the parent of the target device (the one the message is intended for). The message is now sent to the parent (the "path" as above), to be forwarded to our required target device. We prefix the payload with the MAC address of the actual target device and structure it so as to make it easy to decode.&lt;/p&gt;

&lt;p&gt;As an example, let's suppose we want to send a command to turn on the device relay. Let's suppose the MAC address of the device is &lt;code&gt;aa:aa:aa:aa:aa:aa&lt;/code&gt; and the payload to turn on the relay is the single word &lt;code&gt;ON&lt;/code&gt;. If the network is small and the device is close to the hub we can just send it as is, using a function call such as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but if the network is full or the device is physically remote we need to relay the message through another device already in the system, one that is closer to our new device. Let's say the MAC address of this parent device is &lt;code&gt;bb:bb:bb:bb:bb:bb&lt;/code&gt;. So we send a message to that device, and we adjust the payload to be&lt;/p&gt;

&lt;p&gt;&lt;code&gt;!aa:aa:aa:aa:aa:aa,ON&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The function call then becomes (micropython version)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exclamation mark is there to signal that the payload is to be routed to the device whose MAC address is given, and the comma is just a separator. Needless to say, this strategy does not allow payloads to begin with an exclamation mark, so you might wish to use a different marker.&lt;/p&gt;

&lt;p&gt;The code to handle this new message type is remarkably simple. You have to check for the marker, then extract the embedded MAC address and use it to construct a new message to send to the target device. The payload is everything that follows the comma - in this case the original &lt;code&gt;ON&lt;/code&gt; - and the reply from the target is simply passed back to your caller. You can see this done in the &lt;code&gt;receive&lt;/code&gt; function below.&lt;/p&gt;

&lt;p&gt;This technique adds no extra devices to the list of "peers" that are held by any given device, and it can be extended almost indefinitely, by concatenating MAC addresses in the "path" field, such as&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bb:bb:bb:bb:bb:bb,cc:cc:cc:cc:cc:cc&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;which causes the message to be routed through 2 intermediate devices.&lt;/p&gt;

&lt;p&gt;I reproduce below the ESP comms class from a Micropython implementation of the above. It relies on a &lt;code&gt;config&lt;/code&gt; parameter being supplied during initialization, to provide access to other functions in the system, but mostly it just deals with sending and receiving ESP-Now messages. You will notice that the system holds MAC addresses as human-readable strings, whereas ESP-Now requires formatted binaries, so there's a degree of back-and-forth between the two formats.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;binascii&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hexlify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;unhexlify&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;espnow&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ESPNow&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ESPComms&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="c1"&gt;# Default initializer
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setESPComms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nc"&gt;E&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ESP-Now initialised&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Check if a peer is already known
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;checkPeer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nc"&gt;E&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;add_peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Send a message to a given MAC address
&lt;/span&gt;    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;espmsg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;unhexlify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkPeer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Send &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;espmsg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;... to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;E&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;espmsg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Result: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;
                &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nc"&gt;E&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                        &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;E&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;irecv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Received response: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                            &lt;span class="k"&gt;break&lt;/span&gt;
                    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;-=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Response timeout&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Fail&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Fail&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;

    &lt;span class="c1"&gt;# Handle received messages as they arrive
&lt;/span&gt;    &lt;span class="c1"&gt;# Either deal with them or pass them on
&lt;/span&gt;    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Starting ESPNow receiver on channel&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getChannel&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nc"&gt;E&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;E&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;hexlify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Message from &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;comma&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;slave&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;comma&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;comma&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
&lt;span class="c1"&gt;#                    print('Forward to',slave,', msg:',msg)
&lt;/span&gt;                    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slave&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHandler&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="c1"&gt;#                print('Response',response)
&lt;/span&gt;                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkPeer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nc"&gt;E&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kickWatchdog&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Return the RSS (signal strength) of the received message
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getRSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;unhexlify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;E&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;peers_table&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
  </channel>
</rss>
