Сделал обновление программы в МК через радиосвязь на nRF24l01+, Битлэш

toc
Offline
Зарегистрирован: 09.02.2013

arduino wireless program upload RF24 nrf24l01+

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

молодец. где код? :)

toc
Offline
Зарегистрирован: 09.02.2013

...это не совсем то, на что вы, вероятно, рассчитываете...

Введение
Если у вас есть проект на ардуино, и плата застряла в чреве машины, или прикреплена к дереву за окном или недоступна по другим причинам, беспроводная связь для программирования / отладки сэкономит вам кучу времени, вероятно. (portions by ladyada)

Моя цель: сделать работающий прототип, позволяющий загрузить программу в МК  посредством радиосвязи.

Возможные варианты:
1. Uploading Arduino sketches wirelessly using a Bluetooth breakout board.
Минус: у меня нет такого оборудования.
2. Xbee radios, wireless Arduino programming, serial link.
Минус: модули xbee дорогие и у меня их нет.
3. Радиоудлинитель для последовательного порта на nrf24 и МК, обновление программы на основном МК через бутлоадер.  
Минус: дополнительный МК для удлинителя.
Но, мы пойдём другим путём.

Предварительная подготовка читателя этой статьи:
1. Обязательно: уверенная работа в Arduino IDE и успешное освоение примера Blink;
2. Обязательно: успешное выполнение примера pingpair из библиотеки RF24 (от maniacbug) на двух устройствах;
3. Желательно: ознакомиться с Bitlash (от billroy).
О значении неизвестных слов смело спрашивайте гугл, пожалуйста.

Оборудование, которое я использовал:
1. дуина Nano atmega168 плюс модуль nRF24l01+
Всегда подключёно к компьютеру по usb,  далее буду называть этот блок «клиент».
2. дуина Nano atmega328 плюс модуль nRF24l01+
Иногда подключёно к компьютеру по usb, на нём буду обновлять программу, далее буду называть этот блок «сервер».
3. Блок питания 220 Вольт AC – 5 Вольт DC с USB разъёмом.
 
Как это работает:
1. Между клиентом и сервером построен виртуальный радиоудлинитель компорта
2. Весь текст вводимый в мониторе порта клиента, отправляется на сервер через радиосвязь и передаётся посимвольно в битлэш.
3. Текст появляющийся в мониторе порта сервера передаётся клиенту и также выводится в мониторе клиента.
4. Всё остальное — заслуга битлэша.

За дело!
1. Разместим на столе клиент слева, а сервер справа.
2. Запустим arduino.exe два раза. Одно окно разместим слева (для клиента), другое справа (для сервера).
3. Выберем в каждом окне правильный ком порт и правильную плату.
4. Заливаем скетч myRf24BitlashClient_0 на клиент (через usb)

01/*
02 myRf24BitlashClient
03 Copyright (C) 2013 <toc arduino.ru>
04 
05 This program is free software; you can redistribute it and/or
06 modify it under the terms of the GNU General Public License
07 version 2 as published by the Free Software Foundation.
08 */
09 
10#include <SPI.h>
11#include "nRF24L01.h"
12#include "RF24.h"
13#include "printf.h"
14 
15// Set up nRF24L01 radio on SPI bus plus pins 9 & 10
16RF24 radio(9,10);
17 
18// Radio pipe addresses for the 2 nodes to communicate.
19const uint64_t pipes[2] = { 0xF1F0F0F0E1LL, 0xF1F0F0F0D2LL };
20 
21void setup(void)
22{
23  Serial.begin(57600);
24  radio.begin();
25  radio.setRetries(10,15);
26  radio.setPayloadSize(1);
27  radio.openWritingPipe(pipes[0]);
28  radio.openReadingPipe(1,pipes[1]);
29  radio.startListening();
30}
31 
32#define serialPrintByte(b) Serial.write(b)
33 
34byte buf;
35 
36void loop(void)
37{
38  if(radio.available())
39  {
40    radio.read(&buf , 1);
41    serialPrintByte(buf);  
42  }
43   
44  if(Serial.available())
45  {
46    buf = Serial.read();
47    serialPrintByte(buf);
48    radio.stopListening();
49    radio.write(&buf,1);
50    radio.startListening();
51  }
52}

Binary sketch size: 5 492 bytes (of a 14 336 byte maximum)

5. Заливаем скетч myRf24BitlashServer_0 на сервер (через usb)
 

01/*
02 myRf24BitlashServer_0
03 Copyright (C) 2013 <toc arduino.ru>
04  
05 This program is free software; you can redistribute it and/or
06 modify it under the terms of the GNU General Public License
07 version 2 as published by the Free Software Foundation.
08 */
09 
10#include <bitlash.h>
11#include <SPI.h>
12#include "nRF24L01.h"
13#include "RF24.h"
14 
15RF24 radio(9,10);
16 
17const uint64_t pipes[2] = { 0xF1F0F0F0E1LL, 0xF1F0F0F0D2LL };
18 
19void setup(void)
20{
21  initBitlash(57600);
22  radio.begin();
23  radio.setRetries(10,15);
24  radio.setPayloadSize(1);
25  setOutputHandler(&serialHandler);
26  radio.openWritingPipe(pipes[1]);
27  radio.openReadingPipe(1,pipes[0]);
28  radio.startListening();
29}
30 
31#define serialPrintByte(b) Serial.write(b)
32byte buf;
33 
34void serialHandler(byte b)
35{
36  buf=b;
37  serialPrintByte(buf);
38  radio.stopListening();
39  radio.write(&buf,1);
40  radio.startListening();
41}
42 
43void loop(void)
44{
45  if(radio.available())
46  {
47    radio.read(&buf , 1);
48    doCharacter(buf);
49  }
50  else
51  {
52    runBitlash();
53  }
54}

Binary sketch size: 20 498 bytes (of a 32 256 byte maximum)

6. Открываем два монитора порта, слева (для клиента), справа (для сервера). Далее пишем и читаем только в левом мониторе. Правый на всякий случай, для контроля.
7. Вводим в левом мониторе «print 2+3» нажимаем ввод.
8. В обоих мониторах появилось:
print 2+3    
3
>

В принципе, на этом моя цель уже достигнута. Программа “print 2+3” передана по радио на  соседний МК, выполнена, результат получен. Продолжайте читать, дальше чуть интереснее.

Бонус. Исследуем возможности Bitlash
Термины
Скетч — Программа на языке Ардуино, компилируемая и загружаемая во флеш память МК;
Bitlash – библиотека, интерпретатор одноимённого «языка». Скетч myRf24BitlashServer_0 загруженный на сервер содержит битлэш.
Функция Bitlash — Программа на языке Bitlash, интерпретируемая интерактивно при получении из ком порта или загружаемая в eeprom память МК. Осторожно! Битлэш использует EEPROM! Если в вашей еепром памяти есть что-то важное, сохраните в надёжном месте. В моём случае ничего не пропало, просто первая функция получила слишком длинное имя с иероглифами. Похоже, битлэш считает последовательности 0xFF пустым местом, а 0x00 разделителем функций.

1. Вводим в левом мониторе «function printmil{print "mil=",millis();}» нажимаем ввод.
Битлэш отвечает saved – значит функция сохранена в еепром памяти.
2. Вводим в левом мониторе «function startup{run printmil,5000;}» нажимаем ввод.
Битлэш отвечает saved.
3. Команда ls позволяет получить полный список сохранённых функций
4. Нажимаем пальцем резет на правой дуине (сервере).
5. Наблюдаем результат: в мониторах каждые пять секунд появляется новая строка
mil=5035
mil=1080
mil=15122
6. Понимаете, что мы сделали? Мы сохранили в постоянную память сервера программу (набор функций битлэш), которая автоматически выполняется после старта дуины.
По радиоканалу.

Запустить классический Блинк на пятом порту (13-й занят для радио) можно так:

1function toggle5{d5=!d5;}
2run toggle5,1000;

Добавим сенсор
и создадим «пользовательскую функцию».
“Сенсор” будет измерять напряжение питания сервера.

1. Добавим в скетч сервера перед setup это

01numvar readVcc() {
02  // Read 1.1V reference against AVcc
03  // set the reference to Vcc and the measurement to the internal 1.1V reference
04  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
05    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
06  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
07    ADMUX = _BV(MUX5) | _BV(MUX0);
08  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
09    ADMUX = _BV(MUX3) | _BV(MUX2);
10  #else
11    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
12  #endif 
13  
14  delay(2); // Wait for Vref to settle
15  ADCSRA |= _BV(ADSC); // Start conversion
16  while (bit_is_set(ADCSRA,ADSC)); // measuring
17  
18  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH 
19  uint8_t high = ADCH; // unlocks both
20  
21  long result = (high<<8) | low;
22  
23  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
24  return result; // Vcc in millivolts
25}

2. Добавим в скетч сервера, внутрь setup, в начало:

1addBitlashFunction("readvcc", (bitlash_function) readVcc);

3. Зальём скетч в сервер по usb кнопкой upload
4. Вводим в левом мониторе

1function printmil{print "mil=",millis();print "vcc=",readvcc();}

нажимаем ввод.
Битлэш отвечает saved – значит функция сохранена (перезаписана) в еепром памяти.
5. Наблюдаем результат: в мониторах каждые пять секунд появляются новые строки
mil=158234
vcc=5121

Добавим lcd экран
1. Добавим в скетч сервера перед setup это

01#define useLcd
02 
03#if defined(useLcd)
04#include <PCD8544.h>
05PCD8544 nokia = PCD8544(4, 5, 6, 0, 8);
06 
07numvar lcdClear(void) {
08  nokia.clear();
09  nokia.display();
10}
11 
12numvar lcdPrint(void) {
13  for (int i=1; i <= getarg(0); i++) {
14    if (isstringarg(i)) nokia.print((const char *) getstringarg(i));
15    else nokia.print(getarg(i));
16  }
17  nokia.display();
18}
19#endif

2. Добавим в скетч сервера, внутрь setup, в начало:

1#if defined(useLcd)
2  nokia.init(25);
3  nokia.print("myRf24 BitlashServer");
4  nokia.display();
5  addBitlashFunction("lcdclear", (bitlash_function) lcdClear);
6  addBitlashFunction("lcdprint", (bitlash_function) lcdPrint);
7#endif

3. Зальём скетч в сервер по usb кнопкой upload
4. Вводим в левом мониторе lcdprint("Hello, world"); смотрим на экран
5. Вводим в левом мониторе

1function toLcd{ lcdclear;lcdprint("sec=",millis()/1000);lcdprint("\nvcc=",readVcc());}
2function startup {run printmil,5000; run toLcd,1000};

нажимаем ввод.
Битлэш отвечает saved – значит функции сохранены (перезаписана) в еепром памяти.
6. Наблюдаем результат: в мониторах по прежнему каждые пять секунд появляются новые строки
mil=158234
vcc=5121

И на экране появляется то что надо.

Замеченные недостатки:
1. Битлэш занимает 17 килобайт флеш памяти

Замеченные недостатки этого прототипа:
1. Обмен информацией по радиоканалу производится по одному байту, сравнительно медленно
2. Иногда отдельные символы пропадают
3. Дальность радиосвязи на том же оборудовании меньше чем в другом моём проекте. Только что подумал: может плохой канал?
4. Похоже битлэш не может обрабатывать новые команды поступающие по радио в его «последовательный порт» если дуина не подключена к компьютеру. Но, сервер питающийся от розетки успешно выполняет сохранённые в нём битлэш-функции.
5. Похоже, что с библиотекой для экрана осталось мало оперативки. Функция битлэша free() говорит, что около 350 байт. Он начинает подглючивать из-за дифицита памяти.

Todo
1. Исправить недостатки
2. Битлэш умеет запускать функции с sd карты. Надо попробовать.

Timespent:
1. Изучение битлэша и программирование концепта — часа два.
2. Осознание желания поделиться и написание этого текста — неделя.

Возможны ошибки, опечатки, неточности.

toc
Offline
Зарегистрирован: 09.02.2013

std
Offline
Зарегистрирован: 05.01.2012

Круто, с SD было бы любопытно, и может меньше проблем с оперативой будет. Или сделать на Atmega1284, у неё мозгов 16K.

А в идеале конечно подружить с ArduinoISP и писать прямо по ISP, в обход загрузчика. Но это уже наверно с двумя контроллерами в сервере, один прошивающий, другой рабочий.

vlkam
Offline
Зарегистрирован: 17.02.2013

Спасибо!

Подписался на тему

a5021
Offline
Зарегистрирован: 07.07.2013

Материал любопытный и подан доходчиво. Лично мне теперь осталось придумать, где это можно использовать с пользой.

ujuf66
Offline
Зарегистрирован: 18.08.2014

Круто, в идеале передать бы на серевер скетч в HEX формате.

art100
Offline
Зарегистрирован: 09.03.2014

Спасибо.  Чего не прикрутят bitlash.h лишь бы провод не тянуть. А про батареку актуаленько у меня. Попробую прикрутить.